Merge remote branch 'origin/master'

This commit is contained in:
Ekaterina Tuzova
2011-06-01 17:06:01 +04:00
19 changed files with 250 additions and 125 deletions
+2 -5
View File
@@ -48,10 +48,7 @@ class PyDBFrame:
can_skip = (info.pydev_step_cmd is None and info.pydev_step_stop is None)\
or (info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and info.pydev_step_stop is not frame)
if mainDebugger.always_exception_set:
can_skip = False
if mainDebugger.django_breakpoints:
if mainDebugger.always_exception_set or mainDebugger.django_breakpoints or mainDebugger.django_exception_break:
can_skip = False
# Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint,
@@ -97,7 +94,7 @@ class PyDBFrame:
self.setSuspend(thread, CMD_ADD_EXCEPTION_BREAK)
thread.additionalInfo.message = exception_breakpoint.qname
else:
if len(mainDebugger.django_exception_break) and get_exception_name(exception) in ['django.template.base.VariableDoesNotExist', 'django.template.base.TemplateDoesNotExist', 'django.template.base.TemplateSyntaxError'] and just_raised(frame):
if mainDebugger.django_exception_break and get_exception_name(exception) in ['django.template.base.VariableDoesNotExist', 'django.template.base.TemplateDoesNotExist', 'django.template.base.TemplateSyntaxError'] and just_raised(frame):
render_frame = find_django_render_frame(frame)
if render_frame:
suspend_frame = suspend_django(self, mainDebugger, thread, render_frame)
@@ -7,9 +7,9 @@ import com.intellij.psi.ResolveState;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.resolve.CompletionVariantsProcessor;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.resolve.VariantsProcessor;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.Nullable;
@@ -45,7 +45,7 @@ public class PyJavaClassType implements PyType {
}
public Object[] getCompletionVariants(String completionPrefix, PyExpression location, ProcessingContext context) {
final VariantsProcessor processor = new VariantsProcessor(location);
final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(location);
myClass.processDeclarations(processor, ResolveState.initial(), null, location);
return processor.getResult();
}
@@ -39,7 +39,7 @@ public class UnresolvedRefCreateFunctionQuickFix implements LocalQuickFix {
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
if (!CodeInsightUtilBase.preparePsiElementForWrite(myElement)) return;
if (!myElement.isValid() || !CodeInsightUtilBase.preparePsiElementForWrite(myElement)) return;
PyFunctionBuilder functionBuilder = new PyFunctionBuilder(myReference.getText());
@@ -1,5 +1,6 @@
package com.jetbrains.python.codeInsight.intentions;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
@@ -45,6 +46,7 @@ public class PyDocStubIntention extends BaseIntentionAction {
}
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (!CodeInsightUtilBase.preparePsiElementForWrite(file)) return;
PyFunction function = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyFunction.class);
PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
PythonDocumentationProvider documentationProvider = new PythonDocumentationProvider();
@@ -19,6 +19,7 @@ import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.actions.*;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.codeInsight.imports.AutoImportHintAction;
import com.jetbrains.python.codeInsight.imports.AutoImportQuickFix;
@@ -160,6 +161,45 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
return null;
}
private static boolean isGuardedByHasattr(@NotNull final PyElement node, @NotNull final String name) {
final String nodeName = node.getName();
if (nodeName != null) {
final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(node, nodeName);
PyElement e = PsiTreeUtil.getParentOfType(node, PyConditionalStatementPart.class, PyConditionalExpression.class);
while (e != null && PsiTreeUtil.isAncestor(owner, e, true)) {
final ArrayList<PyCallExpression> calls = new ArrayList<PyCallExpression>();
PyExpression cond = null;
if (e instanceof PyConditionalStatementPart) {
cond = ((PyConditionalStatementPart)e).getCondition();
}
else if (e instanceof PyConditionalExpression && PsiTreeUtil.isAncestor(((PyConditionalExpression)e).getTruePart(), node, true)) {
cond = ((PyConditionalExpression)e).getCondition();
}
if (cond instanceof PyCallExpression) {
calls.add((PyCallExpression)cond);
}
if (cond != null) {
final PyCallExpression[] callExprs = PsiTreeUtil.getChildrenOfType(cond, PyCallExpression.class);
if (callExprs != null) {
calls.addAll(Arrays.asList(callExprs));
}
for (PyCallExpression call : calls) {
final PyExpression callee = call.getCallee();
final PyExpression[] args = call.getArguments();
// TODO: Search for `node` aliases using aliases analysis
if (callee != null && "hasattr".equals(callee.getName()) && args.length == 2 &&
nodeName.equals(args[0].getName()) && args[1] instanceof PyStringLiteralExpression &&
((PyStringLiteralExpression)args[1]).getStringValue().equals(name)) {
return true;
}
}
}
e = PsiTreeUtil.getParentOfType(e, PyConditionalStatementPart.class);
}
}
return false;
}
@Override
public void visitPyElement(final PyElement node) {
super.visitPyElement(node);
@@ -194,6 +234,14 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
}
return;
}
if (node instanceof PyQualifiedExpression) {
final PyQualifiedExpression qExpr = (PyQualifiedExpression)node;
final PyExpression qualifier = qExpr.getQualifier();
final String name = node.getName();
if (qualifier != null && name != null && isGuardedByHasattr(qualifier, name)) {
return;
}
}
boolean unresolved;
if (reference instanceof PsiPolyVariantReference) {
final PsiPolyVariantReference poly = (PsiPolyVariantReference)reference;
@@ -7,6 +7,8 @@ import org.jetbrains.annotations.Nullable;
*/
public interface PyConditionalExpression extends PyExpression {
PyExpression getTruePart();
@Nullable
PyExpression getCondition();
@Nullable
@@ -273,7 +273,7 @@ public class PyCallExpressionHelper {
PyType qtype = context.getType(qualifier);
if (qtype instanceof PyClassType) {
if (((PyClassType)qtype).isDefinition()) {
PyClass resolvedParent = PsiTreeUtil.getParentOfType(resolved, PyClass.class);
PyClass resolvedParent = PsiTreeUtil.getStubOrPsiParentOfType(resolved, PyClass.class);
if (resolvedParent != null) {
final PyClass qualifierClass = ((PyClassType)qtype).getPyClass();
if (qualifierClass != null && (qualifierClass.isSubclass(resolvedParent) || resolvedParent.isSubclass(qualifierClass))) {
@@ -37,7 +37,7 @@ public class PyConditionalExpressionImpl extends PyElementImpl implements PyCond
@Override
public PyExpression getCondition() {
final List<PyExpression> expressions = PsiTreeUtil.getChildrenOfTypeAsList(this, PyExpression.class);
return expressions.get(1);
return expressions.size() > 1 ? expressions.get(1) : null;
}
@Override
@@ -1,6 +1,5 @@
package com.jetbrains.python.psi.impl;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.extapi.psi.PsiFileBase;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.vfs.VirtualFile;
@@ -355,21 +354,20 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
@NotNull
public Iterable<PyElement> iterateNames() {
VariantsProcessor processor = new VariantsProcessor(this);
final List<String> dunderAll = getDunderAll();
processor.setAllowedNames(dunderAll);
final List<String> remainingDunderAll = dunderAll == null ? null : new ArrayList<String>(dunderAll);
processDeclarations(processor, ResolveState.initial(), null, this);
List<PyElement> result = new ArrayList<PyElement>();
for (LookupElement lookupElement : processor.getResultList()) {
final Object value = lookupElement.getObject();
if (value instanceof PyElement) {
result.add((PyElement) value);
final List<PyElement> result = new ArrayList<PyElement>();
VariantsProcessor processor = new VariantsProcessor(this) {
@Override
protected void addElement(String name, PsiElement element) {
result.add((PyElement) element);
if (remainingDunderAll != null) {
remainingDunderAll.remove(lookupElement.getLookupString());
remainingDunderAll.remove(name);
}
}
}
};
processor.setAllowedNames(dunderAll);
processDeclarations(processor, ResolveState.initial(), null, this);
if (remainingDunderAll != null) {
for (String s: remainingDunderAll) {
result.add(new LightNamedElement(myManager, PythonLanguage.getInstance(), s));
@@ -148,7 +148,8 @@ public class PyFromImportStatementImpl extends PyBaseElementImpl<PyFromImportSta
else {
PyImportElement[] importElements = getImportElements();
for(PyImportElement element: importElements) {
if (!element.processDeclarations(processor, state, lastParent, place)) {
final PsiElement resolved = ResolveImportUtil.resolveImportElement(element);
if (resolved != null && !processor.execute(resolved, state)) {
return false;
}
}
@@ -180,7 +180,7 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
if (mod_candidate instanceof PyExpression) {
addImportedNames(from_import.getImportElements()); // don't propose already imported items
// collect what's within module file
final VariantsProcessor processor = new VariantsProcessor(myElement, node_filter, null);
final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(myElement, node_filter, null);
processor.setPlainNamesOnly(true); // we don't want parens after functions, etc
PyResolveUtil.treeCrawlUp(processor, true, mod_candidate);
final List<LookupElement> names_from_module = processor.getResultList();
@@ -402,7 +402,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
// include our own names
final int underscores = PyUtil.getInitialUnderscores(myElement.getName());
final VariantsProcessor processor = new VariantsProcessor(myElement);
final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(myElement);
PyResolveUtil.treeCrawlUp(processor, realContext); // names from here
PyResolveUtil.scanOuterContext(processor, realContext); // possible names from around us at call time
@@ -3,7 +3,6 @@ package com.jetbrains.python.psi.impl.stubs;
import com.intellij.psi.stubs.PsiFileStubImpl;
import com.intellij.psi.tree.IStubFileElementType;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.parsing.StatementParsing;
import com.jetbrains.python.psi.FutureFeature;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.impl.PyFileImpl;
@@ -12,8 +11,6 @@ import com.jetbrains.python.psi.stubs.PyFileStub;
import java.util.BitSet;
import java.util.List;
import static com.jetbrains.python.psi.FutureFeature.ABSOLUTE_IMPORT;
/**
* @author yole
*/
@@ -0,0 +1,116 @@
package com.jetbrains.python.psi.resolve;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.Icons;
import com.jetbrains.python.codeInsight.PyClassInsertHandler;
import com.jetbrains.python.codeInsight.PyFunctionInsertHandler;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
/**
* @author yole
*/
public class CompletionVariantsProcessor extends VariantsProcessor {
private final Map<String, LookupElement> myVariants = new HashMap<String, LookupElement>();
public CompletionVariantsProcessor(PsiElement context) {
super(context);
}
public CompletionVariantsProcessor(PsiElement context,
@Nullable Condition<PsiElement> nodeFilter,
@Nullable Condition<String> nameFilter) {
super(context, nodeFilter, nameFilter);
}
protected LookupElementBuilder setupItem(LookupElementBuilder item) {
if (!myPlainNamesOnly) {
if (item.getObject() instanceof PyFunction && ((PyFunction) item.getObject()).getProperty() == null &&
!isSingleArgDecoratorCall(myContext, (PyFunction)item.getObject())) {
item = item.setInsertHandler(PyFunctionInsertHandler.INSTANCE);
final PyParameterList parameterList = ((PyFunction)item.getObject()).getParameterList();
final String params = StringUtil.join(parameterList.getParameters(), new Function<PyParameter, String>() {
@Override
public String fun(PyParameter pyParameter) {
return pyParameter.getText();
}
}, ", ");
item = item.setTailText("(" + params + ")");
}
else if (item.getObject() instanceof PyClass) {
item = item.setInsertHandler(PyClassInsertHandler.INSTANCE);
}
}
if (myNotice != null) {
return setItemNotice(item, myNotice);
}
return item;
}
private static boolean isSingleArgDecoratorCall(PsiElement elementInCall, PyFunction callee) {
if (callee.getParameterList().getParameters().length > 1) {
return false;
}
PyDecorator decorator = PsiTreeUtil.getParentOfType(elementInCall, PyDecorator.class);
if (decorator == null) {
return false;
}
return PsiTreeUtil.isAncestor(decorator.getCallee(), elementInCall, false);
}
protected static LookupElementBuilder setItemNotice(final LookupElementBuilder item, String notice) {
return item.setTypeText(notice);
}
public LookupElement[] getResult() {
final Collection<LookupElement> variants = myVariants.values();
return variants.toArray(new LookupElement[variants.size()]);
}
public List<LookupElement> getResultList() {
return new ArrayList<LookupElement>(myVariants.values());
}
@Override
protected void addElement(String name, PsiElement element) {
myVariants.put(name, setupItem(LookupElementBuilder.create(element, name).setIcon(element.getIcon(0))));
}
protected void addImportedElement(String referencedName, NameDefiner definer, PyElement expr) {
Icon icon = expr.getIcon(0);
// things like PyTargetExpression cannot have a general icon, but here we only have variables
if (icon == null) icon = Icons.VARIABLE_ICON;
LookupElementBuilder lookupItem = setupItem(LookupElementBuilder.create(expr, referencedName).setIcon(icon));
if (definer instanceof PyImportElement) { // set notice to imported module name if needed
PsiElement maybeFromImport = definer.getParent();
if (maybeFromImport instanceof PyFromImportStatement) {
final PyFromImportStatement fromImport = (PyFromImportStatement)maybeFromImport;
PyReferenceExpression src = fromImport.getImportSource();
if (src != null) {
lookupItem = setItemNotice(lookupItem, src.getName());
}
}
}
if (definer instanceof PyAssignmentStatement) {
PyExpression value = ((PyAssignmentStatement)definer).getAssignedValue();
if (value != null) {
PyType type = value.getType(TypeEvalContext.fast());
if (type != null) {
lookupItem = lookupItem.setTypeText(type.getName());
}
}
}
myVariants.put(referencedName, lookupItem);
}
}
@@ -1,31 +1,20 @@
package com.jetbrains.python.psi.resolve;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.Icons;
import com.jetbrains.python.codeInsight.PyClassInsertHandler;
import com.jetbrains.python.codeInsight.PyFunctionInsertHandler;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
public class VariantsProcessor implements PsiScopeProcessor {
private final Map<String, LookupElement> myVariants = new HashMap<String, LookupElement>();
import java.util.ArrayList;
import java.util.List;
public abstract class VariantsProcessor implements PsiScopeProcessor {
protected final PsiElement myContext;
protected String myNotice;
protected Condition<PsiElement> myNodeFilter;
@@ -33,6 +22,7 @@ public class VariantsProcessor implements PsiScopeProcessor {
protected boolean myPlainNamesOnly = false; // if true, add insert handlers to known things like functions
private List<String> myAllowedNames;
private final List<String> mySeenNames = new ArrayList<String>();
public VariantsProcessor(PsiElement context) {
// empty
@@ -58,54 +48,6 @@ public class VariantsProcessor implements PsiScopeProcessor {
}
protected LookupElementBuilder setupItem(LookupElementBuilder item) {
if (!myPlainNamesOnly) {
if (item.getObject() instanceof PyFunction && ((PyFunction) item.getObject()).getProperty() == null &&
!isSingleArgDecoratorCall(myContext, (PyFunction)item.getObject())) {
item = item.setInsertHandler(PyFunctionInsertHandler.INSTANCE);
final PyParameterList parameterList = ((PyFunction)item.getObject()).getParameterList();
final String params = StringUtil.join(parameterList.getParameters(), new Function<PyParameter, String>() {
@Override
public String fun(PyParameter pyParameter) {
return pyParameter.getText();
}
}, ", ");
item = item.setTailText("(" + params + ")");
}
else if (item.getObject() instanceof PyClass) {
item = item.setInsertHandler(PyClassInsertHandler.INSTANCE);
}
}
if (myNotice != null) {
return setItemNotice(item, myNotice);
}
return item;
}
private static boolean isSingleArgDecoratorCall(PsiElement elementInCall, PyFunction callee) {
if (callee.getParameterList().getParameters().length > 1) {
return false;
}
PyDecorator decorator = PsiTreeUtil.getParentOfType(elementInCall, PyDecorator.class);
if (decorator == null) {
return false;
}
return PsiTreeUtil.isAncestor(decorator.getCallee(), elementInCall, false);
}
protected static LookupElementBuilder setItemNotice(final LookupElementBuilder item, String notice) {
return item.setTypeText(notice);
}
public LookupElement[] getResult() {
final Collection<LookupElement> variants = myVariants.values();
return variants.toArray(new LookupElement[variants.size()]);
}
public List<LookupElement> getResultList() {
return new ArrayList<LookupElement>(myVariants.values());
}
public boolean execute(PsiElement element, ResolveState substitutor) {
if (myNodeFilter != null && !myNodeFilter.value(element)) return true; // skip whatever the filter rejects
// TODO: refactor to look saner; much code duplication
@@ -115,28 +57,28 @@ public class VariantsProcessor implements PsiScopeProcessor {
? FileUtil.getNameWithoutExtension(((PyFile)psiNamedElement).getName())
: psiNamedElement.getName();
if (name != null && nameIsAcceptable(name)) {
myVariants.put(name, setupItem(LookupElementBuilder.create(psiNamedElement, name).setIcon(element.getIcon(0))));
addElement(name, psiNamedElement);
}
}
else if (element instanceof PyReferenceExpression) {
PyReferenceExpression expr = (PyReferenceExpression)element;
String referencedName = expr.getReferencedName();
if (nameIsAcceptable(referencedName)) {
myVariants.put(referencedName, setupItem(LookupElementBuilder.create(expr, referencedName)));
addElement(referencedName, expr);
}
}
else if (element instanceof NameDefiner) {
boolean handled_as_imported = false;
if (element instanceof PyImportElement) {
final PyImportElement importElement = (PyImportElement)element;
PyReferenceExpression ref = importElement.getImportReference();
if (ref != null && ref.getQualifier() == null) {
String name = importElement.getAsName() != null ? importElement.getAsName() : ref.getName();
final PyQualifiedName qName = importElement.getImportedQName();
if (qName != null && qName.getComponentCount() == 1) {
String name = importElement.getAsName() != null ? importElement.getAsName() : qName.getLastComponent();
if (name != null && nameIsAcceptable(name)) {
PsiElement resolved = ref.getReference().resolve();
final PsiElement resolved = ResolveImportUtil.resolveImportElement(importElement);
if (resolved instanceof PsiNamedElement) {
handled_as_imported = true;
myVariants.put(name, setupItem(LookupElementBuilder.create(resolved, name).setIcon(resolved.getIcon(0))));
addElement(name, resolved);
}
}
}
@@ -146,31 +88,8 @@ public class VariantsProcessor implements PsiScopeProcessor {
for (PyElement expr : definer.iterateNames()) {
if (expr != null && expr != myContext) { // NOTE: maybe rather have SingleIterables skip nulls outright?
String referencedName = expr.getName();
Icon icon = expr.getIcon(0);
// things like PyTargetExpression cannot have a general icon, but here we only have variables
if (icon == null) icon = Icons.VARIABLE_ICON;
if (referencedName != null && nameIsAcceptable(referencedName)) {
LookupElementBuilder lookupItem = setupItem(LookupElementBuilder.create(expr, referencedName).setIcon(icon));
if (definer instanceof PyImportElement) { // set notice to imported module name if needed
PsiElement maybeFromImport = definer.getParent();
if (maybeFromImport instanceof PyFromImportStatement) {
final PyFromImportStatement fromImport = (PyFromImportStatement)maybeFromImport;
PyReferenceExpression src = fromImport.getImportSource();
if (src != null) {
lookupItem = setItemNotice(lookupItem, src.getName());
}
}
}
if (definer instanceof PyAssignmentStatement) {
PyExpression value = ((PyAssignmentStatement)definer).getAssignedValue();
if (value != null) {
PyType type = value.getType(TypeEvalContext.fast());
if (type != null) {
lookupItem = lookupItem.setTypeText(type.getName());
}
}
}
myVariants.put(referencedName, lookupItem);
addImportedElement(referencedName, definer, expr);
}
}
}
@@ -180,11 +99,19 @@ public class VariantsProcessor implements PsiScopeProcessor {
return true;
}
protected void addElement(String name, PsiElement psiNamedElement) {
mySeenNames.add(name);
}
protected void addImportedElement(String referencedName, NameDefiner definer, PyElement expr) {
addElement(referencedName, expr);
}
private boolean nameIsAcceptable(String name) {
if (name == null) {
return false;
}
if (myVariants.containsKey(name)) {
if (mySeenNames.contains(name)) {
return false;
}
if (myNameFilter != null && !myNameFilter.value(name)) {
@@ -228,7 +228,7 @@ public class PyClassType extends UserDataHolderBase implements PyType {
}
boolean withinOurClass = containingClass == getPyClass();
final VariantsProcessor processor = new VariantsProcessor(
final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(
expressionHook, new PyResolveUtil.FilterNotInstance(myClass), null
);
processor.setNotice(myClass.getName());
@@ -14,10 +14,10 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.PyDynamicMember;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.ResolveResultList;
import com.jetbrains.python.psi.resolve.CompletionVariantsProcessor;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.VariantsProcessor;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -112,7 +112,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
}
if (point == ResolveImportUtil.PointInImport.NONE || point == ResolveImportUtil.PointInImport.AS_NAME) { // when not imported from, add regular attributes
final VariantsProcessor processor = new VariantsProcessor(location, new Condition<PsiElement>() {
final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(location, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement psiElement) {
return !(psiElement instanceof PyImportElement);
@@ -0,0 +1,33 @@
class C(object):
def __init__(self):
self.foo = 1
def f(self):
self.bar = 2
def g(self):
if hasattr(self, 'baz'):
return self.baz #pass
else:
return self.spam if hasattr(self, 'spam') else 'eggs' #pass
def main():
c = C()
c2 = C()
try:
if hasattr(c2, 'x'):
d1 = c.<warning descr="Unresolved attribute reference 'x' for class 'C'">x</warning> #fail
d2 = c2.x #pass
return d1, d2
if hasattr(c, 'spam'):
def inner():
c = C()
return c.<warning descr="Unresolved attribute reference 'spam' for class 'C'">spam</warning> #fail
return inner() + c.spam #pass
if hasattr(c, 'f'):
return c.f() #pass
return c.<warning descr="Unresolved attribute reference 'spam' for class 'C'">spam</warning> #fail
finally:
if hasattr(c, 'close'):
c.close() #pass
@@ -44,6 +44,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyLightFixtureTestCase
myFixture.checkHighlighting(true, false, false);
}
public void testHasattrGuard() { // PY-2309
doTest();
}
private void doTest() {
myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py");
myFixture.enableInspections(PyUnresolvedReferencesInspection.class);