mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-02 00:57:59 +07:00
Merge remote branch 'origin/master'
This commit is contained in:
@@ -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
|
||||
|
||||
+4
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user