mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -10,11 +10,10 @@ import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.psi.impl.PyQualifiedName;
|
||||
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedNameResolver;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.IOException;
|
||||
@@ -108,14 +107,14 @@ public abstract class QtFileType implements FileType, INativeFileType {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String findToolInPackage(String toolName, Module module, Sdk sdk, String name) {
|
||||
List<PsiElement> elements = ResolveImportUtil.resolveModulesInRootProvider(sdk.getRootProvider(), module, PyQualifiedName.fromComponents(name));
|
||||
for (PsiElement psiElement : elements) {
|
||||
if (psiElement instanceof PsiDirectory) {
|
||||
VirtualFile tool = ((PsiDirectory)psiElement).getVirtualFile().findChild(toolName + ".exe");
|
||||
if (tool != null) {
|
||||
return tool.getPath();
|
||||
}
|
||||
QualifiedNameResolver visitor = new QualifiedNameResolver(name).fromModule(module).withSdk(sdk);
|
||||
List<PsiDirectory> elements = visitor.resultsOfType(PsiDirectory.class);
|
||||
for (PsiDirectory directory : elements) {
|
||||
VirtualFile tool = directory.getVirtualFile().findChild(toolName + ".exe");
|
||||
if (tool != null) {
|
||||
return tool.getPath();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -13,7 +13,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyImportReferenceImpl;
|
||||
import com.jetbrains.python.psi.impl.PyImportReference;
|
||||
import com.jetbrains.python.sdk.IronPythonSdkFlavor;
|
||||
import com.jetbrains.python.sdk.PySkeletonRefresher;
|
||||
import com.jetbrains.python.sdk.PythonSdkFlavor;
|
||||
@@ -92,7 +92,7 @@ public class GenerateBinaryStubsFix implements LocalQuickFix {
|
||||
}
|
||||
|
||||
public static boolean isApplicable(PsiReference ref) {
|
||||
if (!(ref instanceof PyImportReferenceImpl)) {
|
||||
if (!(ref instanceof PyImportReference)) {
|
||||
return false;
|
||||
}
|
||||
final Sdk sdk = getPythonSdk(ref);
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ReplaceBuiltinsQuickFix implements LocalQuickFix {
|
||||
PsiElement importStatement = descriptor.getPsiElement();
|
||||
if (importStatement instanceof PyImportStatement) {
|
||||
for (PyImportElement importElement : ((PyImportStatement)importStatement).getImportElements()) {
|
||||
PyReferenceExpression importReference = importElement.getImportReference();
|
||||
PyReferenceExpression importReference = importElement.getImportReferenceExpression();
|
||||
if (importReference != null) {
|
||||
if ("__builtin__".equals(importReference.getName())) {
|
||||
importReference.replace(elementGenerator.createFromText(LanguageLevel.getDefault(), PyReferenceExpression.class, "builtins"));
|
||||
|
||||
@@ -108,7 +108,7 @@ public class PyDynamicMember {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement resolve(PsiElement context) {
|
||||
public PsiElement resolve(@NotNull PsiElement context) {
|
||||
if (myTarget != null) {
|
||||
return myTarget;
|
||||
}
|
||||
@@ -124,7 +124,7 @@ public class PyDynamicMember {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement findResolveTarget(PsiElement context) {
|
||||
private PsiElement findResolveTarget(@NotNull PsiElement context) {
|
||||
if (myPsiPath != null) {
|
||||
return myPsiPath.resolve(context);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package com.jetbrains.python.codeInsight;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyQualifiedName;
|
||||
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedNameResolver;
|
||||
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -26,8 +24,8 @@ public abstract class PyPsiPath {
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve(PsiElement context) {
|
||||
final List<PsiElement> elements = ResolveImportUtil.resolveModulesInRoots(myQualifiedName, context);
|
||||
return elements.size() > 0 ? elements.get(0) : null;
|
||||
QualifiedNameResolver visitor = new QualifiedNameResolver(myQualifiedName).fromElement(context);
|
||||
return visitor.firstResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
if (refExpr.getQualifier() == null) {
|
||||
final PsiPolyVariantReference reference = refExpr.getReference();
|
||||
if (reference.resolve() == null) {
|
||||
AutoImportQuickFix fix = proposeImportFix(refExpr, reference, refExpr.getText());
|
||||
AutoImportQuickFix fix = proposeImportFix(refExpr, reference);
|
||||
if (fix != null && fix.getCandidatesCount() == 1) {
|
||||
fix.invoke(file);
|
||||
}
|
||||
@@ -67,14 +67,17 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
private static TokenSet IS_IMPORT_STATEMENT = TokenSet.create(PyElementTypes.IMPORT_STATEMENT);
|
||||
|
||||
@Nullable
|
||||
public static AutoImportQuickFix proposeImportFix(final PyElement node, PsiReference reference, String ref_text) {
|
||||
public static AutoImportQuickFix proposeImportFix(final PyElement node, PsiReference reference) {
|
||||
final String text = reference.getElement().getText();
|
||||
final String refText = reference.getRangeInElement().substring(text); // text of the part we're working with
|
||||
|
||||
// don't propose meaningless auto imports if no interpreter is configured
|
||||
final Module module = ModuleUtil.findModuleForPsiElement(node);
|
||||
if (module != null && PythonSdkType.findPythonSdk(module) == null) {
|
||||
return null;
|
||||
}
|
||||
PsiFile existing_import_file = null; // if there's a matching existing import, this it the file it imports
|
||||
AutoImportQuickFix fix = new AutoImportQuickFix(node, reference, ref_text, !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT);
|
||||
AutoImportQuickFix fix = new AutoImportQuickFix(node, reference, refText, !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT);
|
||||
Set<String> seen_file_names = new HashSet<String>(); // true import names
|
||||
// maybe the name is importable via some existing 'import foo' statement, and only needs a qualifier.
|
||||
// walk up collecting all such statements and analyzing
|
||||
@@ -84,14 +87,14 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
if (result.size() > 0) {
|
||||
for (PsiElement stmt : import_prc.getResult()) {
|
||||
for (PyImportElement ielt : ((PyImportStatement)stmt).getImportElements()) {
|
||||
final PyReferenceExpression src = ielt.getImportReference();
|
||||
final PyReferenceExpression src = ielt.getImportReferenceExpression();
|
||||
if (src != null) {
|
||||
PsiElement dst = src.getReference().resolve();
|
||||
if (dst instanceof PyFile) {
|
||||
PyFile dst_file = (PyFile)dst;
|
||||
String name = ielt.getImportReference().getReferencedName(); // ref is ok or matching would fail
|
||||
String name = ielt.getImportReferenceExpression().getReferencedName(); // ref is ok or matching would fail
|
||||
seen_file_names.add(name);
|
||||
PsiElement res = dst_file.findExportedName(ref_text);
|
||||
PsiElement res = dst_file.findExportedName(refText);
|
||||
if (res != null && !(res instanceof PyFile) && !(res instanceof PyImportElement) && dst_file.equals(res.getContainingFile())) {
|
||||
existing_import_file = dst_file;
|
||||
fix.addImport(res, dst_file, ielt);
|
||||
@@ -107,14 +110,14 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
// NOTE: current indices have limitations, only finding direct definitions of classes and functions.
|
||||
Project project = node.getProject();
|
||||
List<PsiElement> symbols = new ArrayList<PsiElement>();
|
||||
symbols.addAll(PyClassNameIndex.find(ref_text, project, true));
|
||||
symbols.addAll(PyClassNameIndex.find(refText, project, true));
|
||||
GlobalSearchScope scope = PyClassNameIndex.projectWithLibrariesScope(project);
|
||||
if (!isQualifier(node)) {
|
||||
symbols.addAll(PyFunctionNameIndex.find(ref_text, project, scope));
|
||||
symbols.addAll(PyFunctionNameIndex.find(refText, project, scope));
|
||||
}
|
||||
symbols.addAll(PyVariableNameIndex.find(ref_text, project, scope));
|
||||
symbols.addAll(PyVariableNameIndex.find(refText, project, scope));
|
||||
if (!isCall(node)) {
|
||||
symbols.addAll(findImportableModules(node.getContainingFile(), ref_text, project, scope));
|
||||
symbols.addAll(findImportableModules(node.getContainingFile(), refText, project, scope));
|
||||
}
|
||||
if (symbols.size() > 0) {
|
||||
for (PsiElement symbol : symbols) {
|
||||
@@ -125,7 +128,7 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
PyQualifiedName import_path = ResolveImportUtil.findCanonicalImportPath(srcfile, node);
|
||||
if (import_path != null && !seen_file_names.contains(import_path.toString())) {
|
||||
// a new, valid hit
|
||||
fix.addImport(symbol, srcfile, import_path, proposeAsName(node.getContainingFile(), ref_text, import_path));
|
||||
fix.addImport(symbol, srcfile, import_path, proposeAsName(node.getContainingFile(), refText, import_path));
|
||||
seen_file_names.add(import_path.toString()); // just in case, again
|
||||
}
|
||||
}
|
||||
@@ -133,7 +136,7 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
}
|
||||
}
|
||||
for(PyImportCandidateProvider provider: Extensions.getExtensions(PyImportCandidateProvider.EP_NAME)) {
|
||||
provider.addImportCandidates(reference, ref_text, fix);
|
||||
provider.addImportCandidates(reference, refText, fix);
|
||||
}
|
||||
if (fix.getCandidatesCount() > 0) {
|
||||
fix.sortCandidates();
|
||||
|
||||
+5
-5
@@ -72,7 +72,7 @@ public class ImportFromToImportIntention implements IntentionAction {
|
||||
ret.myModuleReference = ret.myFromImportStatement.getImportSource();
|
||||
}
|
||||
if (ret.myModuleReference != null) {
|
||||
ret.myModuleName = PyResolveUtil.toPath(ret.myModuleReference, ".");
|
||||
ret.myModuleName = PyResolveUtil.toPath(ret.myModuleReference);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ public class ImportFromToImportIntention implements IntentionAction {
|
||||
if (info.myModuleReference != null) {
|
||||
PyExpression remaining_module = info.myModuleReference.getQualifier();
|
||||
if (remaining_module instanceof PyQualifiedExpression) {
|
||||
remaining_name = PyResolveUtil.toPath((PyQualifiedExpression)remaining_module, ".");
|
||||
remaining_name = PyResolveUtil.toPath((PyQualifiedExpression)remaining_module);
|
||||
}
|
||||
else remaining_name = ""; // unqualified name: "...module"
|
||||
separated_name = info.myModuleReference.getReferencedName();
|
||||
@@ -142,7 +142,7 @@ public class ImportFromToImportIntention implements IntentionAction {
|
||||
if (info.myRelativeLevel > 0) {
|
||||
// make sure we aren't importing a module from the relative path
|
||||
for (PyImportElement import_element : info.myFromImportStatement.getImportElements()) {
|
||||
PyReferenceExpression ref = import_element.getImportReference();
|
||||
PyReferenceExpression ref = import_element.getImportReferenceExpression();
|
||||
if (ref != null && ref.isValid()) {
|
||||
PsiElement target = ref.getReference().resolve();
|
||||
if (target instanceof PyExpression && ((PyExpression)target).getType(TypeEvalContext.fast()) instanceof PyModuleType) return false;
|
||||
@@ -152,7 +152,7 @@ public class ImportFromToImportIntention implements IntentionAction {
|
||||
}
|
||||
}
|
||||
if (info.myModuleReference != null) {
|
||||
info.myModuleName = PyResolveUtil.toPath(info.myModuleReference, ".");
|
||||
info.myModuleName = PyResolveUtil.toPath(info.myModuleReference);
|
||||
}
|
||||
if (info.myModuleReference != null && info.myModuleName != null && info.myFromImportStatement != null) {
|
||||
myText = info.getText();
|
||||
@@ -246,7 +246,7 @@ public class ImportFromToImportIntention implements IntentionAction {
|
||||
PyImportElement ielt = entry.getValue();
|
||||
if (ielt.getAsNameElement() != null) {
|
||||
// we have an alias, replace it with real name
|
||||
PyReferenceExpression refex = ielt.getImportReference();
|
||||
PyReferenceExpression refex = ielt.getImportReferenceExpression();
|
||||
assert refex != null; // else we won't resolve to this ielt
|
||||
String real_name = refex.getReferencedName();
|
||||
ASTNode new_qualifier = generator.createExpressionFromText(real_name).getNode();
|
||||
|
||||
+6
-6
@@ -77,9 +77,9 @@ public class ImportToImportFromIntention implements IntentionAction {
|
||||
assert myImportElement != null : "isAvailable() must have returned true, but myImportElement is null";
|
||||
|
||||
// usages of imported name are qualifiers; what they refer to?
|
||||
PyReferenceExpression reference = myImportElement.getImportReference();
|
||||
PyReferenceExpression reference = myImportElement.getImportReferenceExpression();
|
||||
if (reference != null) {
|
||||
myModuleName = PyResolveUtil.toPath(reference, ".");
|
||||
myModuleName = PyResolveUtil.toPath(reference);
|
||||
myQualifierName = myImportElement.getVisibleName();
|
||||
myReferee = reference.getReference().resolve();
|
||||
myHasModuleReference = false;
|
||||
@@ -89,7 +89,7 @@ public class ImportToImportFromIntention implements IntentionAction {
|
||||
public boolean execute(@NotNull PsiElement element) {
|
||||
if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element, PyImportElement.class) == null) {
|
||||
PyReferenceExpression ref = (PyReferenceExpression)element;
|
||||
if (myQualifierName.equals(PyResolveUtil.toPath(ref, "."))) { // filter out other names that might resolve to our target
|
||||
if (myQualifierName.equals(PyResolveUtil.toPath(ref))) { // filter out other names that might resolve to our target
|
||||
PsiElement parent_elt = ref.getParent();
|
||||
if (parent_elt instanceof PyQualifiedExpression) { // really qualified by us, not just referencing?
|
||||
PsiElement resolved = ref.getReference().resolve();
|
||||
@@ -108,7 +108,7 @@ public class ImportToImportFromIntention implements IntentionAction {
|
||||
|
||||
public void invoke() {
|
||||
assert myImportElement != null : "isAvailable() must have returned true, but myImportElement is null";
|
||||
PyUtil.sure(myImportElement.getImportReference());
|
||||
PyUtil.sure(myImportElement.getImportReferenceExpression());
|
||||
Project project = myImportElement.getProject();
|
||||
|
||||
// usages of imported name are qualifiers; what they refer to?
|
||||
@@ -181,8 +181,8 @@ public class ImportToImportFromIntention implements IntentionAction {
|
||||
public String getText() {
|
||||
String module_name = "?";
|
||||
if (myImportElement != null) {
|
||||
PyReferenceExpression reference = myImportElement.getImportReference();
|
||||
if (reference != null) module_name = PyResolveUtil.toPath(reference, ".");
|
||||
PyReferenceExpression reference = myImportElement.getImportReferenceExpression();
|
||||
if (reference != null) module_name = PyResolveUtil.toPath(reference);
|
||||
}
|
||||
return PyBundle.message("INTN.convert.to.from.$0.import.$1", getDots()+module_name, "...");
|
||||
}
|
||||
|
||||
+5
-5
@@ -64,7 +64,7 @@ public class ImportToggleAliasIntention implements IntentionAction {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
final PyReferenceExpression referenceExpression = myImportElement.getImportReference();
|
||||
final PyReferenceExpression referenceExpression = myImportElement.getImportReferenceExpression();
|
||||
if (referenceExpression == null || referenceExpression.getReference().resolve() == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class ImportToggleAliasIntention implements IntentionAction {
|
||||
public String getText() {
|
||||
String add_name = "Add alias";
|
||||
if (myImportElement != null) {
|
||||
PyReferenceExpression refex = myImportElement.getImportReference();
|
||||
PyReferenceExpression refex = myImportElement.getImportReferenceExpression();
|
||||
if (refex != null) {
|
||||
add_name = PyBundle.message("INTN.add.alias.for.import.$0", refex.getText());
|
||||
}
|
||||
@@ -108,10 +108,10 @@ public class ImportToggleAliasIntention implements IntentionAction {
|
||||
//
|
||||
final String target_name; // we set in in the source
|
||||
final String remove_name; // we replace it in the source
|
||||
PyReferenceExpression reference = sure(state.myImportElement.getImportReference());
|
||||
PyReferenceExpression reference = sure(state.myImportElement.getImportReferenceExpression());
|
||||
// search for references to us with the right name
|
||||
try {
|
||||
String imported_name = PyResolveUtil.toPath(reference, ".");
|
||||
String imported_name = PyResolveUtil.toPath(reference);
|
||||
if (state.myAlias != null) {
|
||||
// have to remove alias, rename everything to original
|
||||
target_name = imported_name;
|
||||
@@ -150,7 +150,7 @@ public class ImportToggleAliasIntention implements IntentionAction {
|
||||
public boolean execute(@NotNull PsiElement element) {
|
||||
if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element, PyImportElement.class) == null) {
|
||||
PyReferenceExpression ref = (PyReferenceExpression)element;
|
||||
if (remove_name.equals(PyResolveUtil.toPath(ref, "."))) { // filter out other names that might resolve to our target
|
||||
if (remove_name.equals(PyResolveUtil.toPath(ref))) { // filter out other names that might resolve to our target
|
||||
PsiElement resolved = ref.getReference().resolve();
|
||||
if (resolved == referee) references.add(ref.getReference());
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class DocStringTypeReference extends PsiReferenceBase<PsiElement> {
|
||||
}
|
||||
if (myType instanceof PyImportedModuleType) {
|
||||
final PyImportedModule module = ((PyImportedModuleType)myType).getImportedModule();
|
||||
return PyUtil.turnDirIntoInit(ResolveImportUtil.resolveInRoots(module.getContainingFile(), module.getImportedPrefix()));
|
||||
return PyUtil.turnDirIntoInit(ResolveImportUtil.resolveModuleInRoots(module.getImportedPrefix(), module.getContainingFile()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,7 @@ import com.jetbrains.python.console.PyConsoleUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyCallExpressionHelper;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
|
||||
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
|
||||
import com.jetbrains.python.psi.resolve.RootVisitor;
|
||||
import com.jetbrains.python.psi.resolve.*;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
@@ -498,7 +495,7 @@ class DocumentationBuilder {
|
||||
}
|
||||
else {
|
||||
RootFinder finder = new RootFinder(path);
|
||||
ResolveImportUtil.visitRoots(followed, finder);
|
||||
RootVisitorHost.visitRoots(followed, finder);
|
||||
final String root_path = finder.getResult();
|
||||
if (root_path != null) {
|
||||
String after_part = path.substring(root_path.length());
|
||||
|
||||
@@ -14,8 +14,6 @@ import com.jetbrains.python.psi.types.PyTypeReference;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -79,8 +77,8 @@ public class PyUsageTypeProvider implements UsageTypeProviderEx {
|
||||
final PsiElement element = ((PsiElementUsageTarget)targets[0]).getElement();
|
||||
if (element instanceof PyFunction) {
|
||||
PyFunction function = (PyFunction) element;
|
||||
final Set<PyFunction.Flag> flags = PyUtil.detectDecorationsAndWrappersOf(function);
|
||||
PyCallExpression.PyMarkedCallee callee = new PyCallExpression.PyMarkedCallee(function, flags, 1, true);
|
||||
final PyFunction.Modifier modifier = function.getModifier();
|
||||
PyCallExpression.PyMarkedCallee callee = new PyCallExpression.PyMarkedCallee(function, modifier, 1, true);
|
||||
CallArgumentsMappingImpl mapping = new CallArgumentsMappingImpl(call.getArgumentList());
|
||||
mapping.mapArguments(callee, null);
|
||||
if (mapping.hasProblems()) {
|
||||
|
||||
@@ -14,8 +14,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.jetbrains.python.psi.PyFunction.Flag.CLASSMETHOD;
|
||||
import static com.jetbrains.python.psi.PyFunction.Flag.STATICMETHOD;
|
||||
import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD;
|
||||
import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD;
|
||||
|
||||
/**
|
||||
* Checks for for calls like <code>X.method(y,...)</code>, where y is not an instance of X.
|
||||
@@ -77,7 +77,7 @@ public class PyCallByClassInspection extends PyInspection {
|
||||
if (arglist != null) {
|
||||
CallArgumentsMapping analysis = arglist.analyzeCall(resolveWithoutImplicits());
|
||||
final PyCallExpression.PyMarkedCallee markedCallee = analysis.getMarkedCallee();
|
||||
if (markedCallee != null && !markedCallee.getFlags().contains(STATICMETHOD)) {
|
||||
if (markedCallee != null && markedCallee.getModifier() != STATICMETHOD) {
|
||||
PyParameter[] params = markedCallee.getCallable().getParameterList().getParameters();
|
||||
if (params.length > 0 && params[0] instanceof PyNamedParameter) {
|
||||
PyNamedParameter first_param = (PyNamedParameter)params[0];
|
||||
@@ -89,7 +89,7 @@ public class PyCallByClassInspection extends PyInspection {
|
||||
PyType first_arg_type = myTypeEvalContext.getType(first_arg);
|
||||
if (first_arg_type instanceof PyClassType) {
|
||||
final PyClassType first_arg_class_type = (PyClassType)first_arg_type;
|
||||
if (first_arg_class_type.isDefinition() && !markedCallee.getFlags().contains(CLASSMETHOD)) {
|
||||
if (first_arg_class_type.isDefinition() && markedCallee.getModifier() != CLASSMETHOD) {
|
||||
registerProblem(
|
||||
first_arg,
|
||||
PyBundle.message("INSP.instance.of.$0.excpected", qual_class.getQualifiedName())
|
||||
|
||||
+2
-3
@@ -12,7 +12,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Reports assignment to 'self' or 'cls'.
|
||||
@@ -64,8 +63,8 @@ public class PyMethodFirstArgAssignmentInspection extends PyInspection {
|
||||
final String first_param_name = first_parm.getName();
|
||||
if (first_param_name == null || first_param_name.length() < 1) return null; // ignore cases of incorrect code
|
||||
// is it a static method?
|
||||
Set<PyFunction.Flag> flagSet = PyUtil.detectDecorationsAndWrappersOf(method);
|
||||
if (flagSet.contains(PyFunction.Flag.STATICMETHOD)) return null; // these may do whatever they please
|
||||
PyFunction.Modifier modifier = method.getModifier();
|
||||
if (modifier == PyFunction.Modifier.STATICMETHOD) return null; // these may do whatever they please
|
||||
return first_param_name;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.actions.AddSelfQuickFix;
|
||||
import com.jetbrains.python.actions.RenameParameterQuickFix;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyQualifiedName;
|
||||
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -56,7 +57,7 @@ public class PyMethodParametersInspection extends PyInspection {
|
||||
synchronized (this) { // other threads would wait as long in resolveInRoots() anyway
|
||||
if (myPossibleZopeRef == null) {
|
||||
myPossibleZopeRef = new Ref<PsiElement>();
|
||||
ret = ResolveImportUtil.resolveInRoots(foothold, "zope.interface.Interface");
|
||||
ret = ResolveImportUtil.resolveModuleInRoots(PyQualifiedName.fromComponents("zope.interface.Interface"), foothold);
|
||||
myPossibleZopeRef.set(ret); // null is OK
|
||||
}
|
||||
else ret = myPossibleZopeRef.get();
|
||||
|
||||
+139
-113
@@ -40,7 +40,7 @@ import com.jetbrains.python.console.PydevConsoleRunner;
|
||||
import com.jetbrains.python.documentation.DocStringParameterReference;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyImportReferenceImpl;
|
||||
import com.jetbrains.python.psi.impl.PyImportReference;
|
||||
import com.jetbrains.python.psi.impl.PyImportStatementNavigator;
|
||||
import com.jetbrains.python.psi.impl.PyOperatorReferenceImpl;
|
||||
import com.jetbrains.python.psi.resolve.ImportedResolveResult;
|
||||
@@ -290,7 +290,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
myAllImports.remove(node.getParent());
|
||||
}
|
||||
}
|
||||
else if (reference instanceof PyImportReferenceImpl &&
|
||||
else if (reference instanceof PyImportReference &&
|
||||
target == reference.getElement().getContainingFile()) {
|
||||
final boolean insideFromImport = PsiTreeUtil.getParentOfType(node, PyFromImportStatement.class) != null;
|
||||
if ((insideFromImport && PyImportStatementNavigator.getImportStatementByElement(node) != null) || !insideFromImport) {
|
||||
@@ -323,11 +323,10 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
}
|
||||
|
||||
private void registerUnresolvedReferenceProblem(final PyElement node, final PsiReference reference, HighlightSeverity severity) {
|
||||
final StringBuilder description_buf = new StringBuilder(""); // TODO: clear description_buf logic. maybe a flag is needed instead.
|
||||
String description = null;
|
||||
final String text = reference.getElement().getText();
|
||||
final String ref_text = reference.getRangeInElement().substring(text); // text of the part we're working with
|
||||
final PsiElement element = reference.getElement();
|
||||
final boolean ref_is_importable = PythonReferenceImporter.isImportable(element);
|
||||
final List<LocalQuickFix> actions = new ArrayList<LocalQuickFix>(2);
|
||||
if (ref_text.length() <= 0) return; // empty text, nothing to highlight
|
||||
final String refname = (element instanceof PyQualifiedExpression) ? ((PyQualifiedExpression)element).getReferencedName() : ref_text;
|
||||
@@ -342,42 +341,16 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
if (refex.getQualifier() != null) {
|
||||
final PyClassType object_type = PyBuiltinCache.getInstance(node).getObjectType();
|
||||
if ((object_type != null) && object_type.getPossibleInstanceMembers().contains(refname)) return;
|
||||
|
||||
}
|
||||
else {
|
||||
if (LanguageLevel.forElement(node).isOlderThan(LanguageLevel.PYTHON26)) {
|
||||
if (refname.equals("with")) {
|
||||
if ("with".equals(refname)) {
|
||||
actions.add(new UnresolvedRefAddFutureImportQuickFix(refex));
|
||||
}
|
||||
}
|
||||
PyClass containedClass = PsiTreeUtil.getParentOfType(node, PyClass.class);
|
||||
if (containedClass != null) {
|
||||
for (PyTargetExpression target : containedClass.getInstanceAttributes()) {
|
||||
if (Comparing.strEqual(node.getName(), target.getName())) {
|
||||
actions.add(new UnresolvedReferenceAddSelfQuickFix(refex));
|
||||
}
|
||||
}
|
||||
for (PyStatement statement : containedClass.getStatementList().getStatements()) {
|
||||
if (statement instanceof PyAssignmentStatement) {
|
||||
if (((PyAssignmentStatement)statement).getLeftHandSideExpression().getText().equals(refex.getText())) {
|
||||
PyExpression callexpr = ((PyAssignmentStatement)statement).getAssignedValue();
|
||||
if (callexpr instanceof PyCallExpression) {
|
||||
PyType type = myTypeEvalContext.getType(callexpr);
|
||||
if (type != null && type instanceof PyClassType) {
|
||||
String name = ((PyCallExpression)callexpr).getCallee().getText();
|
||||
if (name != null && name.equals("property"))
|
||||
actions.add(new UnresolvedReferenceAddSelfQuickFix(refex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PyFunction method : containedClass.getMethods()) {
|
||||
if (refex.getText().equals(method.getName())) {
|
||||
actions.add(new UnresolvedReferenceAddSelfQuickFix(refex));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ref_text.equals("true") || ref_text.equals("false"))
|
||||
actions.add(new UnresolvedRefTrueFalseQuickFix(element));
|
||||
addAddSelfFix(node, refex, actions);
|
||||
PyCallExpression callExpression = PsiTreeUtil.getParentOfType(element, PyCallExpression.class);
|
||||
if (callExpression != null)
|
||||
actions.add(new UnresolvedRefCreateFunctionQuickFix(callExpression, refex));
|
||||
@@ -392,8 +365,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
) != null
|
||||
)) {
|
||||
severity = HighlightSeverity.WEAK_WARNING;
|
||||
String errmsg = PyBundle.message("INSP.module.$0.not.found", ref_text);
|
||||
description_buf.append(errmsg);
|
||||
description = PyBundle.message("INSP.module.$0.not.found", ref_text);
|
||||
// TODO: mark the node so that future references pointing to it won't result in a error, but in a warning
|
||||
}
|
||||
}
|
||||
@@ -401,11 +373,10 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
if (myIgnoredIdentifiers.contains(reference.getCanonicalText()))
|
||||
return;
|
||||
}
|
||||
if (reference instanceof PsiReferenceEx) {
|
||||
final String s = ((PsiReferenceEx)reference).getUnresolvedDescription();
|
||||
if (s != null) description_buf.append(s);
|
||||
if (reference instanceof PsiReferenceEx && description == null) {
|
||||
description = ((PsiReferenceEx)reference).getUnresolvedDescription();
|
||||
}
|
||||
if (description_buf.length() == 0) {
|
||||
if (description == null) {
|
||||
boolean marked_qualified = false;
|
||||
if (element instanceof PyQualifiedExpression) {
|
||||
final PyQualifiedExpression qexpr = (PyQualifiedExpression)element;
|
||||
@@ -416,100 +387,39 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
if (qualifier != null) {
|
||||
PyType qtype = myTypeEvalContext.getType(qualifier);
|
||||
if (qtype != null) {
|
||||
if (qtype instanceof PyNoneType || qtype instanceof PyTypeReference ||
|
||||
(qtype instanceof PyUnionType && ((PyUnionType) qtype).isWeak()) ||
|
||||
(qtype instanceof PyImportedModuleType)) {
|
||||
// this almost always means that we don't know the type, so don't show an error in this case
|
||||
if (ignoreUnresolvedMemberForType(qtype, reference, ref_text)) {
|
||||
return;
|
||||
}
|
||||
addCreateMemberFromUsageFixes(qtype, reference, ref_text, actions);
|
||||
if (qtype instanceof PyClassType) {
|
||||
PyClass cls = ((PyClassType)qtype).getPyClass();
|
||||
if (cls != null) {
|
||||
if (overridesGetAttr(cls)) {
|
||||
return;
|
||||
}
|
||||
if (cls.findProperty(ref_text) != null) {
|
||||
return; // a property exists but accessor is not found; other inspections handle this
|
||||
}
|
||||
if (! PyBuiltinCache.getInstance(node).hasInBuiltins(cls)) {
|
||||
if (element.getParent() instanceof PyCallExpression) {
|
||||
actions.add(new AddMethodQuickFix(ref_text, (PyClassType)qtype));
|
||||
}
|
||||
else if (!(reference instanceof PyOperatorReferenceImpl)) {
|
||||
actions.add(new AddFieldQuickFix(ref_text, cls, "None"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reference instanceof PyOperatorReferenceImpl) {
|
||||
description_buf.append(PyBundle.message("INSP.unresolved.operator.ref",
|
||||
qtype.getName(), refname,
|
||||
((PyOperatorReferenceImpl)reference).getReadableOperatorName()));
|
||||
description = PyBundle.message("INSP.unresolved.operator.ref",
|
||||
qtype.getName(), refname,
|
||||
((PyOperatorReferenceImpl)reference).getReadableOperatorName());
|
||||
}
|
||||
else {
|
||||
description_buf.append(PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", ref_text, qtype.getName()));
|
||||
description = PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", ref_text, qtype.getName());
|
||||
}
|
||||
marked_qualified = true;
|
||||
}
|
||||
else if (qtype instanceof PyModuleType) {
|
||||
PsiFile file = ((PyModuleType)qtype).getModule();
|
||||
if (file instanceof PyFile) {
|
||||
actions.add(new AddFunctionQuickFix(ref_text, (PyFile)file));
|
||||
}
|
||||
}
|
||||
else if (qtype instanceof CythonBuiltinType ||
|
||||
(qtype instanceof CythonType && reference instanceof PyOperatorReferenceImpl)) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
description_buf.append(PyBundle.message("INSP.cannot.find.$0.in.$1", ref_text, qtype.getName()));
|
||||
description = PyBundle.message("INSP.cannot.find.$0.in.$1", ref_text, qtype.getName());
|
||||
marked_qualified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! marked_qualified) {
|
||||
description_buf.append(PyBundle.message("INSP.unresolved.ref.$0", ref_text));
|
||||
if (ref_text.equals("true") || ref_text.equals("false"))
|
||||
actions.add(new UnresolvedRefTrueFalseQuickFix(element));
|
||||
if (!marked_qualified) {
|
||||
description = PyBundle.message("INSP.unresolved.ref.$0", ref_text);
|
||||
|
||||
// look in other imported modules for this whole name
|
||||
if (ref_is_importable) {
|
||||
AutoImportQuickFix importFix = PythonReferenceImporter.proposeImportFix(node, reference, ref_text);
|
||||
if (importFix != null) {
|
||||
// if the context doesn't look like a function call and we only found imports of functions, suggest auto-import
|
||||
// as a quickfix but no popup balloon (PY-2312)
|
||||
if ((isCall(node) || !importFix.hasOnlyFunctions()) && PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP) {
|
||||
final AutoImportHintAction autoImportHintAction = new AutoImportHintAction(importFix);
|
||||
actions.add(autoImportHintAction);
|
||||
}
|
||||
else {
|
||||
actions.add(importFix);
|
||||
}
|
||||
}
|
||||
if (PythonReferenceImporter.isImportable(element)) {
|
||||
addAutoImportFix(node, reference, actions);
|
||||
}
|
||||
|
||||
if (ref_text.length() > 2 && Character.isUpperCase(ref_text.charAt(0)) && !Character.isUpperCase(ref_text.charAt(1)) &&
|
||||
PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) == null) {
|
||||
PsiElement anchor = element;
|
||||
if (element instanceof PyQualifiedExpression) {
|
||||
final PyExpression qexpr = ((PyQualifiedExpression)element).getQualifier();
|
||||
if (qexpr != null) {
|
||||
final PyType type = myTypeEvalContext.getType(qexpr);
|
||||
if (type instanceof PyModuleType) {
|
||||
anchor = ((PyModuleType) type).getModule();
|
||||
}
|
||||
else {
|
||||
anchor = null;
|
||||
}
|
||||
}
|
||||
if (anchor != null) {
|
||||
actions.add(new CreateClassQuickFix(ref_text, anchor));
|
||||
}
|
||||
}
|
||||
}
|
||||
addCreateClassFix(ref_text, element, actions);
|
||||
}
|
||||
}
|
||||
String description = description_buf.toString();
|
||||
ProblemHighlightType hl_type;
|
||||
if (severity == HighlightSeverity.WARNING) {
|
||||
hl_type = ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
|
||||
@@ -535,6 +445,122 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
registerProblem(point, description, hl_type, null, range, actions.toArray(new LocalQuickFix[actions.size()]));
|
||||
}
|
||||
|
||||
private static boolean ignoreUnresolvedMemberForType(PyType qtype, PsiReference reference, String refText) {
|
||||
if (qtype instanceof PyNoneType || qtype instanceof PyTypeReference ||
|
||||
(qtype instanceof PyUnionType && ((PyUnionType)qtype).isWeak()) ||
|
||||
(qtype instanceof PyImportedModuleType)) {
|
||||
// this almost always means that we don't know the type, so don't show an error in this case
|
||||
return true;
|
||||
}
|
||||
if (qtype instanceof PyClassType) {
|
||||
PyClass cls = ((PyClassType)qtype).getPyClass();
|
||||
if (cls != null) {
|
||||
if (overridesGetAttr(cls)) {
|
||||
return true;
|
||||
}
|
||||
if (cls.findProperty(refText) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (qtype instanceof CythonBuiltinType ||
|
||||
(qtype instanceof CythonType && reference instanceof PyOperatorReferenceImpl)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void addCreateMemberFromUsageFixes(PyType qtype, PsiReference reference, String refText, List<LocalQuickFix> actions) {
|
||||
PsiElement element = reference.getElement();
|
||||
if (qtype instanceof PyClassType) {
|
||||
PyClass cls = ((PyClassType)qtype).getPyClass();
|
||||
if (cls != null) {
|
||||
if (!PyBuiltinCache.getInstance(element).hasInBuiltins(cls)) {
|
||||
if (element.getParent() instanceof PyCallExpression) {
|
||||
actions.add(new AddMethodQuickFix(refText, (PyClassType)qtype));
|
||||
}
|
||||
else if (!(reference instanceof PyOperatorReferenceImpl)) {
|
||||
actions.add(new AddFieldQuickFix(refText, cls, "None"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (qtype instanceof PyModuleType) {
|
||||
PsiFile file = ((PyModuleType)qtype).getModule();
|
||||
if (file instanceof PyFile) {
|
||||
actions.add(new AddFunctionQuickFix(refText, (PyFile)file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addAddSelfFix(PyElement node, PyReferenceExpression refex, List<LocalQuickFix> actions) {
|
||||
PyClass containedClass = PsiTreeUtil.getParentOfType(node, PyClass.class);
|
||||
if (containedClass != null) {
|
||||
for (PyTargetExpression target : containedClass.getInstanceAttributes()) {
|
||||
if (Comparing.strEqual(node.getName(), target.getName())) {
|
||||
actions.add(new UnresolvedReferenceAddSelfQuickFix(refex));
|
||||
}
|
||||
}
|
||||
for (PyStatement statement : containedClass.getStatementList().getStatements()) {
|
||||
if (statement instanceof PyAssignmentStatement) {
|
||||
if (((PyAssignmentStatement)statement).getLeftHandSideExpression().getText().equals(refex.getText())) {
|
||||
PyExpression callexpr = ((PyAssignmentStatement)statement).getAssignedValue();
|
||||
if (callexpr instanceof PyCallExpression) {
|
||||
PyType type = myTypeEvalContext.getType(callexpr);
|
||||
if (type != null && type instanceof PyClassType) {
|
||||
String name = ((PyCallExpression)callexpr).getCallee().getText();
|
||||
if (name != null && name.equals("property"))
|
||||
actions.add(new UnresolvedReferenceAddSelfQuickFix(refex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PyFunction method : containedClass.getMethods()) {
|
||||
if (refex.getText().equals(method.getName())) {
|
||||
actions.add(new UnresolvedReferenceAddSelfQuickFix(refex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addAutoImportFix(PyElement node, PsiReference reference, List<LocalQuickFix> actions) {
|
||||
AutoImportQuickFix importFix = PythonReferenceImporter.proposeImportFix(node, reference);
|
||||
if (importFix != null) {
|
||||
// if the context doesn't look like a function call and we only found imports of functions, suggest auto-import
|
||||
// as a quickfix but no popup balloon (PY-2312)
|
||||
if ((isCall(node) || !importFix.hasOnlyFunctions()) && PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP) {
|
||||
final AutoImportHintAction autoImportHintAction = new AutoImportHintAction(importFix);
|
||||
actions.add(autoImportHintAction);
|
||||
}
|
||||
else {
|
||||
actions.add(importFix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addCreateClassFix(String refText, PsiElement element, List<LocalQuickFix> actions) {
|
||||
if (refText.length() > 2 && Character.isUpperCase(refText.charAt(0)) && !Character.isUpperCase(refText.charAt(1)) &&
|
||||
PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) == null) {
|
||||
PsiElement anchor = element;
|
||||
if (element instanceof PyQualifiedExpression) {
|
||||
final PyExpression qexpr = ((PyQualifiedExpression)element).getQualifier();
|
||||
if (qexpr != null) {
|
||||
final PyType type = myTypeEvalContext.getType(qexpr);
|
||||
if (type instanceof PyModuleType) {
|
||||
anchor = ((PyModuleType) type).getModule();
|
||||
}
|
||||
else {
|
||||
anchor = null;
|
||||
}
|
||||
}
|
||||
if (anchor != null) {
|
||||
actions.add(new CreateClassQuickFix(refText, anchor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCall(PyElement node) {
|
||||
final PyCallExpression callExpression = PsiTreeUtil.getParentOfType(node, PyCallExpression.class);
|
||||
return callExpression != null && node == callExpression.getCallee();
|
||||
|
||||
@@ -245,7 +245,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor {
|
||||
if (PsiTreeUtil.getParentOfType(element, PyClass.class) != null) {
|
||||
// When function is inside a class, first parameter may be either self or cls which is always 'used'.
|
||||
final PyFunction method = PsiTreeUtil.getParentOfType(element, PyFunction.class);
|
||||
if (method != null && ! PyNames.STATICMETHOD.equals(PyUtil.getClassOrStaticMethodDecorator(method))) {
|
||||
if (method != null && method.getModifier() != PyFunction.Modifier.STATICMETHOD) {
|
||||
final PsiElement parent = namedParameter.getParent();
|
||||
if (parent instanceof PyParameterList && ((PyParameterList)parent).getParameters()[0] == namedParameter) {
|
||||
continue;
|
||||
|
||||
@@ -5,9 +5,6 @@ import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents an entire call expression, like <tt>foo()</tt> or <tt>foo.bar[1]('x')</tt>.
|
||||
*/
|
||||
@@ -85,7 +82,7 @@ public interface PyCallExpression extends PyExpression {
|
||||
*/
|
||||
class PyMarkedCallee {
|
||||
Callable myCallable;
|
||||
Set<PyFunction.Flag> myFlags;
|
||||
PyFunction.Modifier myModifier;
|
||||
int myImplicitOffset;
|
||||
boolean myImplicitlyResolved;
|
||||
|
||||
@@ -97,16 +94,16 @@ public interface PyCallExpression extends PyExpression {
|
||||
* @param offset implicit argument offset; parameters up to this are implicitly filled in the call.
|
||||
* @param implicitlyResolved value for {@link #isImplicitlyResolved()}
|
||||
*/
|
||||
public PyMarkedCallee(@NotNull Callable function, Set<PyFunction.Flag> flags, int offset, boolean implicitlyResolved) {
|
||||
public PyMarkedCallee(@NotNull Callable function, PyFunction.Modifier modifier, int offset, boolean implicitlyResolved) {
|
||||
myCallable = function;
|
||||
myFlags = flags;
|
||||
myModifier = modifier;
|
||||
myImplicitOffset = offset;
|
||||
myImplicitlyResolved = implicitlyResolved;
|
||||
}
|
||||
|
||||
public PyMarkedCallee(Callable callable, boolean implicitlyResolved) {
|
||||
myCallable = callable;
|
||||
myFlags = EnumSet.noneOf(PyFunction.Flag.class);
|
||||
myModifier = null;
|
||||
myImplicitOffset = 0;
|
||||
myImplicitlyResolved = implicitlyResolved;
|
||||
}
|
||||
@@ -115,8 +112,8 @@ public interface PyCallExpression extends PyExpression {
|
||||
return myCallable;
|
||||
}
|
||||
|
||||
public Set<PyFunction.Flag> getFlags() {
|
||||
return myFlags;
|
||||
public PyFunction.Modifier getModifier() {
|
||||
return myModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,10 +63,18 @@ extends
|
||||
*/
|
||||
String getDeprecationMessage();
|
||||
|
||||
/**
|
||||
* Looks for two standard decorators to a function, or a wrapping assignment that closely follows it.
|
||||
*
|
||||
* @return a flag describing what was detected.
|
||||
*/
|
||||
@Nullable
|
||||
Modifier getModifier();
|
||||
|
||||
/**
|
||||
* Flags that mark common alterations of a function: decoration by and wrapping in classmethod() and staticmethod().
|
||||
*/
|
||||
enum Flag {
|
||||
enum Modifier {
|
||||
/**
|
||||
* Function is decorated with @classmethod, its first param is the class.
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public interface PyImportElement extends PyElement, NameDefiner, StubBasedPsiElement<PyImportElementStub> {
|
||||
@Nullable
|
||||
PyReferenceExpression getImportReference();
|
||||
PyReferenceExpression getImportReferenceExpression();
|
||||
|
||||
@Nullable
|
||||
PyQualifiedName getImportedQName();
|
||||
|
||||
@@ -17,13 +17,11 @@ import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.popup.Balloon;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
@@ -39,9 +37,6 @@ import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType;
|
||||
import com.jetbrains.python.documentation.EpydocUtil;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyQualifiedName;
|
||||
import com.jetbrains.python.psi.stubs.PyFunctionStub;
|
||||
import com.jetbrains.python.psi.stubs.PyTargetExpressionStub;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.types.PyTupleType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
@@ -58,9 +53,8 @@ import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import static com.jetbrains.python.psi.PyFunction.Flag.CLASSMETHOD;
|
||||
import static com.jetbrains.python.psi.PyFunction.Flag.STATICMETHOD;
|
||||
import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.interpretAsStaticmethodOrClassmethodWrappingCall;
|
||||
import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD;
|
||||
import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD;
|
||||
|
||||
public class PyUtil {
|
||||
private PyUtil() {
|
||||
@@ -443,42 +437,6 @@ public class PyUtil {
|
||||
if (!thing) throw new IncorrectOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* When a function is decorated many decorators, finds the deepest builtin decorator:
|
||||
* <pre>
|
||||
* @foo
|
||||
* @classmethod <b># <-- that's it</b>
|
||||
* @bar
|
||||
* def moo(cls):
|
||||
* pass
|
||||
* </pre>
|
||||
* @param node the allegedly decorated function
|
||||
* @return name of the built-in decorator, or null (even if there are non-built-in decorators).
|
||||
*/
|
||||
@Nullable
|
||||
public static String getClassOrStaticMethodDecorator(@NotNull final PyFunction node) {
|
||||
PyDecoratorList decolist = node.getDecoratorList();
|
||||
if (decolist != null) {
|
||||
PyDecorator[] decos = decolist.getDecorators();
|
||||
if (decos.length > 0) {
|
||||
for (int i = decos.length - 1; i >= 0; i -= 1) {
|
||||
PyDecorator deco = decos[i];
|
||||
String deconame = deco.getName();
|
||||
if (PyNames.CLASSMETHOD.equals(deconame) || PyNames.STATICMETHOD.equals(deconame)) {
|
||||
return deconame;
|
||||
}
|
||||
for(PyKnownDecoratorProvider provider: KnownDecoratorProviderHolder.KNOWN_DECORATOR_PROVIDERS) {
|
||||
String name = provider.toKnownDecorator(deconame);
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isInstanceAttribute(PyExpression target) {
|
||||
if (!(target instanceof PyTargetExpression)) {
|
||||
return false;
|
||||
@@ -612,80 +570,6 @@ public class PyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for two standard decorators to a function, or a wrapping assignment that closely follows it.
|
||||
*
|
||||
* @param function what to analyze
|
||||
* @return a set of flags describing what was detected.
|
||||
*/
|
||||
@NotNull
|
||||
public static Set<PyFunction.Flag> detectDecorationsAndWrappersOf(PyFunction function) {
|
||||
Set<PyFunction.Flag> flags = EnumSet.noneOf(PyFunction.Flag.class);
|
||||
String deconame = getClassOrStaticMethodDecorator(function);
|
||||
if (PyNames.CLASSMETHOD.equals(deconame)) {
|
||||
flags.add(CLASSMETHOD);
|
||||
}
|
||||
else if (PyNames.STATICMETHOD.equals(deconame)) flags.add(STATICMETHOD);
|
||||
// implicit staticmethod __new__
|
||||
PyClass cls = function.getContainingClass();
|
||||
if (cls != null && PyNames.NEW.equals(function.getName()) && cls.isNewStyleClass()) flags.add(STATICMETHOD);
|
||||
//
|
||||
if (!flags.contains(CLASSMETHOD) && !flags.contains(STATICMETHOD)) { // not set by decos, look for reassignment
|
||||
final PyFunctionStub stub = function.getStub();
|
||||
if (stub != null) {
|
||||
return getWrappersFromStub(stub);
|
||||
}
|
||||
String func_name = function.getName();
|
||||
if (func_name != null) {
|
||||
PyAssignmentStatement assignment = PsiTreeUtil.getNextSiblingOfType(function, PyAssignmentStatement.class);
|
||||
if (assignment != null) {
|
||||
for (Pair<PyExpression, PyExpression> pair : assignment.getTargetsToValuesMapping()) {
|
||||
PyExpression value = pair.getSecond();
|
||||
if (value instanceof PyCallExpression) {
|
||||
PyExpression target = pair.getFirst();
|
||||
if (target instanceof PyTargetExpression && func_name.equals(target.getName())) {
|
||||
Pair<String, PyFunction> interpreted = interpretAsStaticmethodOrClassmethodWrappingCall((PyCallExpression)value, function);
|
||||
if (interpreted != null) {
|
||||
PyFunction original = interpreted.getSecond();
|
||||
if (original == function) {
|
||||
String wrapper_name = interpreted.getFirst();
|
||||
if (PyNames.CLASSMETHOD.equals(wrapper_name)) {
|
||||
flags.add(CLASSMETHOD);
|
||||
}
|
||||
else if (PyNames.STATICMETHOD.equals(wrapper_name)) flags.add(STATICMETHOD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
private static Set<PyFunction.Flag> getWrappersFromStub(PyFunctionStub stub) {
|
||||
final StubElement parentStub = stub.getParentStub();
|
||||
final List childrenStubs = parentStub.getChildrenStubs();
|
||||
int index = childrenStubs.indexOf(stub);
|
||||
if (index >= 0 && index < childrenStubs.size() - 1) {
|
||||
StubElement nextStub = (StubElement) childrenStubs.get(index+1);
|
||||
if (nextStub instanceof PyTargetExpressionStub) {
|
||||
final PyTargetExpressionStub targetExpressionStub = (PyTargetExpressionStub)nextStub;
|
||||
if (targetExpressionStub.getInitializerType() == PyTargetExpressionStub.InitializerType.CallExpression) {
|
||||
final PyQualifiedName qualifiedName = targetExpressionStub.getInitializer();
|
||||
if (PyQualifiedName.fromComponents(PyNames.CLASSMETHOD).equals(qualifiedName)) {
|
||||
return EnumSet.of(PyFunction.Flag.CLASSMETHOD);
|
||||
}
|
||||
if (PyQualifiedName.fromComponents(PyNames.STATICMETHOD).equals(qualifiedName)) {
|
||||
return EnumSet.of(PyFunction.Flag.STATICMETHOD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return EnumSet.noneOf(PyFunction.Flag.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns child element in the psi tree
|
||||
*
|
||||
@@ -999,7 +883,7 @@ public class PyUtil {
|
||||
public static MethodFlags of(@NotNull PyFunction node) {
|
||||
PyClass cls = node.getContainingClass();
|
||||
if (cls != null) {
|
||||
Set<PyFunction.Flag> flags = detectDecorationsAndWrappersOf(node);
|
||||
PyFunction.Modifier modifier = node.getModifier();
|
||||
boolean isMetaclassMethod = false;
|
||||
PyClass type_cls = PyBuiltinCache.getInstance(node).getClass("type");
|
||||
for (PyClass ancestor_cls : cls.iterateAncestorClasses()) {
|
||||
@@ -1010,7 +894,7 @@ public class PyUtil {
|
||||
}
|
||||
final String method_name = node.getName();
|
||||
boolean isSpecialMetaclassMethod = isMetaclassMethod && method_name != null && among(method_name, PyNames.INIT, "__call__");
|
||||
return new MethodFlags(flags.contains(CLASSMETHOD), flags.contains(STATICMETHOD), isMetaclassMethod, isSpecialMetaclassMethod);
|
||||
return new MethodFlags(modifier == CLASSMETHOD, modifier == STATICMETHOD, isMetaclassMethod, isSpecialMetaclassMethod);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class PyCallExpressionHelper {
|
||||
|
||||
@Nullable
|
||||
public static PyCallExpression.PyMarkedCallee resolveCallee(PyCallExpression us, PyResolveContext resolveContext, int implicitOffset) {
|
||||
PyFunction.Flag wrappedFlag = null;
|
||||
PyFunction.Modifier wrappedModifier = null;
|
||||
boolean isConstructorCall = false;
|
||||
|
||||
PyExpression callee = us.getCallee();
|
||||
@@ -167,17 +167,17 @@ public class PyCallExpressionHelper {
|
||||
resolved = wrapperInfo.getSecond();
|
||||
String wrapper_name = wrapperInfo.getFirst();
|
||||
if (PyNames.CLASSMETHOD.equals(wrapper_name)) {
|
||||
wrappedFlag = PyFunction.Flag.CLASSMETHOD;
|
||||
wrappedModifier = PyFunction.Modifier.CLASSMETHOD;
|
||||
}
|
||||
else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrappedFlag = PyFunction.Flag.STATICMETHOD;
|
||||
else if (PyNames.STATICMETHOD.equals(wrapper_name)) wrappedModifier = PyFunction.Modifier.STATICMETHOD;
|
||||
}
|
||||
}
|
||||
if (resolved instanceof Callable) {
|
||||
Set<PyFunction.Flag> flags = resolved instanceof PyFunction
|
||||
? PyUtil.detectDecorationsAndWrappersOf((PyFunction) resolved)
|
||||
: EnumSet.noneOf(PyFunction.Flag.class);
|
||||
if (wrappedFlag != null) {
|
||||
flags.add(wrappedFlag);
|
||||
PyFunction.Modifier modifier = resolved instanceof PyFunction
|
||||
? ((PyFunction)resolved).getModifier()
|
||||
: null;
|
||||
if (modifier == null && wrappedModifier != null) {
|
||||
modifier = wrappedModifier;
|
||||
}
|
||||
List<PyExpression> qualifiers = resolveResult != null ? resolveResult.getQualifiers() : Collections.<PyExpression>emptyList();
|
||||
boolean isByInstance = isConstructorCall ||
|
||||
@@ -187,16 +187,16 @@ public class PyCallExpressionHelper {
|
||||
boolean isByClass = lastQualifier == null ? false : isQualifiedByClass((Callable)resolved, lastQualifier, resolveContext.getTypeEvalContext());
|
||||
final Callable callable = (Callable)resolved;
|
||||
|
||||
implicitOffset += getImplicitArgumentCount(callable, flags, isConstructorCall, isByInstance, isByClass);
|
||||
implicitOffset += getImplicitArgumentCount(callable, modifier, isConstructorCall, isByInstance, isByClass);
|
||||
implicitOffset = implicitOffset < 0? 0: implicitOffset; // wrong source can trigger strange behaviour
|
||||
return new PyCallExpression.PyMarkedCallee(callable, flags, implicitOffset,
|
||||
return new PyCallExpression.PyMarkedCallee(callable, modifier, implicitOffset,
|
||||
resolveResult != null ? resolveResult.isImplicit() : false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the {@link #getImplicitArgumentCount(PyExpression, Callable, PyFunction.Flag, EnumSet<PyFunction.Flag>, boolean) full version}
|
||||
* Calls the {@link #getImplicitArgumentCount(PyExpression, Callable, com.jetbrains.python.psi.PyFunction.Modifier, EnumSet< com.jetbrains.python.psi.PyFunction.Modifier >, boolean) full version}
|
||||
* with null flags and with isByInstance inferred directly from call site (won't work with reassigned bound methods).
|
||||
*
|
||||
* @param callReference the call site, where arguments are given.
|
||||
@@ -215,7 +215,7 @@ public class PyCallExpressionHelper {
|
||||
QualifiedResolveResult followed = callReference.followAssignmentsChain(resolveContext);
|
||||
boolean isByInstance = isQualifiedByInstance(functionBeingCalled, followed.getQualifiers(), resolveContext.getTypeEvalContext());
|
||||
boolean isByClass = isQualifiedByInstance(functionBeingCalled, followed.getQualifiers(), resolveContext.getTypeEvalContext());
|
||||
return getImplicitArgumentCount(functionBeingCalled, PyUtil.detectDecorationsAndWrappersOf(functionBeingCalled), false, isByInstance, isByClass);
|
||||
return getImplicitArgumentCount(functionBeingCalled, functionBeingCalled.getModifier(), false, isByInstance, isByClass);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,7 +229,7 @@ public class PyCallExpressionHelper {
|
||||
*/
|
||||
private static int getImplicitArgumentCount(
|
||||
Callable callable,
|
||||
Set<PyFunction.Flag> flags,
|
||||
PyFunction.Modifier modifier,
|
||||
boolean isConstructorCall,
|
||||
boolean isByInstance,
|
||||
boolean isByClass
|
||||
@@ -247,10 +247,10 @@ public class PyCallExpressionHelper {
|
||||
}
|
||||
|
||||
// decorators?
|
||||
if (flags.contains(PyFunction.Flag.STATICMETHOD)) {
|
||||
if (modifier == PyFunction.Modifier.STATICMETHOD) {
|
||||
if (isByInstance && implicit_offset > 0) implicit_offset -= 1; // might have marked it as implicit 'self'
|
||||
}
|
||||
else if (flags.contains(PyFunction.Flag.CLASSMETHOD)) {
|
||||
else if (modifier == PyFunction.Modifier.CLASSMETHOD) {
|
||||
if (!isByInstance) implicit_offset += 1; // Both Foo.method() and foo.method() have implicit the first arg
|
||||
}
|
||||
return implicit_offset;
|
||||
|
||||
@@ -119,7 +119,7 @@ public class PyDecoratorImpl extends PyPresentableElementImpl<PyDecoratorStub> i
|
||||
if (callee == null) return null;
|
||||
if (!hasArgumentList()) {
|
||||
// NOTE: that +1 thing looks fishy
|
||||
callee = new PyMarkedCallee(callee.getCallable(), callee.getFlags(), callee.getImplicitOffset() + 1, callee.isImplicitlyResolved());
|
||||
callee = new PyMarkedCallee(callee.getCallable(), callee.getModifier(), callee.getImplicitOffset() + 1, callee.isImplicitlyResolved());
|
||||
}
|
||||
return callee;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -25,6 +26,7 @@ import com.jetbrains.python.documentation.StructuredDocString;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.stubs.PyClassStub;
|
||||
import com.jetbrains.python.psi.stubs.PyFunctionStub;
|
||||
import com.jetbrains.python.psi.stubs.PyTargetExpressionStub;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -33,6 +35,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD;
|
||||
import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD;
|
||||
import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.interpretAsStaticmethodOrClassmethodWrappingCall;
|
||||
|
||||
/**
|
||||
* Implements PyFunction.
|
||||
*/
|
||||
@@ -416,4 +422,115 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
|
||||
}
|
||||
return super.getUseScope();
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for two standard decorators to a function, or a wrapping assignment that closely follows it.
|
||||
*
|
||||
* @return a flag describing what was detected.
|
||||
*/
|
||||
@Nullable
|
||||
public Modifier getModifier() {
|
||||
String deconame = getClassOrStaticMethodDecorator();
|
||||
if (PyNames.CLASSMETHOD.equals(deconame)) {
|
||||
return CLASSMETHOD;
|
||||
}
|
||||
else if (PyNames.STATICMETHOD.equals(deconame)) {
|
||||
return STATICMETHOD;
|
||||
}
|
||||
// implicit staticmethod __new__
|
||||
PyClass cls = getContainingClass();
|
||||
if (cls != null && PyNames.NEW.equals(getName()) && cls.isNewStyleClass()) {
|
||||
return STATICMETHOD;
|
||||
}
|
||||
//
|
||||
if (getStub() != null) {
|
||||
return getWrappersFromStub();
|
||||
}
|
||||
String func_name = getName();
|
||||
if (func_name != null) {
|
||||
PyAssignmentStatement assignment = PsiTreeUtil.getNextSiblingOfType(this, PyAssignmentStatement.class);
|
||||
if (assignment != null) {
|
||||
for (Pair<PyExpression, PyExpression> pair : assignment.getTargetsToValuesMapping()) {
|
||||
PyExpression value = pair.getSecond();
|
||||
if (value instanceof PyCallExpression) {
|
||||
PyExpression target = pair.getFirst();
|
||||
if (target instanceof PyTargetExpression && func_name.equals(target.getName())) {
|
||||
Pair<String, PyFunction> interpreted = interpretAsStaticmethodOrClassmethodWrappingCall((PyCallExpression)value, this);
|
||||
if (interpreted != null) {
|
||||
PyFunction original = interpreted.getSecond();
|
||||
if (original == this) {
|
||||
String wrapper_name = interpreted.getFirst();
|
||||
if (PyNames.CLASSMETHOD.equals(wrapper_name)) {
|
||||
return CLASSMETHOD;
|
||||
}
|
||||
else if (PyNames.STATICMETHOD.equals(wrapper_name)) {
|
||||
return STATICMETHOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Modifier getWrappersFromStub() {
|
||||
final StubElement parentStub = getStub().getParentStub();
|
||||
final List childrenStubs = parentStub.getChildrenStubs();
|
||||
int index = childrenStubs.indexOf(getStub());
|
||||
if (index >= 0 && index < childrenStubs.size() - 1) {
|
||||
StubElement nextStub = (StubElement) childrenStubs.get(index+1);
|
||||
if (nextStub instanceof PyTargetExpressionStub) {
|
||||
final PyTargetExpressionStub targetExpressionStub = (PyTargetExpressionStub)nextStub;
|
||||
if (targetExpressionStub.getInitializerType() == PyTargetExpressionStub.InitializerType.CallExpression) {
|
||||
final PyQualifiedName qualifiedName = targetExpressionStub.getInitializer();
|
||||
if (PyQualifiedName.fromComponents(PyNames.CLASSMETHOD).equals(qualifiedName)) {
|
||||
return Modifier.CLASSMETHOD;
|
||||
}
|
||||
if (PyQualifiedName.fromComponents(PyNames.STATICMETHOD).equals(qualifiedName)) {
|
||||
return Modifier.STATICMETHOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* When a function is decorated many decorators, finds the deepest builtin decorator:
|
||||
* <pre>
|
||||
* @foo
|
||||
* @classmethod <b># <-- that's it</b>
|
||||
* @bar
|
||||
* def moo(cls):
|
||||
* pass
|
||||
* </pre>
|
||||
* @return name of the built-in decorator, or null (even if there are non-built-in decorators).
|
||||
*/
|
||||
@Nullable
|
||||
private String getClassOrStaticMethodDecorator() {
|
||||
PyDecoratorList decolist = getDecoratorList();
|
||||
if (decolist != null) {
|
||||
PyDecorator[] decos = decolist.getDecorators();
|
||||
if (decos.length > 0) {
|
||||
for (int i = decos.length - 1; i >= 0; i -= 1) {
|
||||
PyDecorator deco = decos[i];
|
||||
String deconame = deco.getName();
|
||||
if (PyNames.CLASSMETHOD.equals(deconame) || PyNames.STATICMETHOD.equals(deconame)) {
|
||||
return deconame;
|
||||
}
|
||||
for(PyKnownDecoratorProvider provider: PyUtil.KnownDecoratorProviderHolder.KNOWN_DECORATOR_PROVIDERS) {
|
||||
String name = provider.toKnownDecorator(deconame);
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class PyImportElementImpl extends PyBaseElementImpl<PyImportElementStub>
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PyReferenceExpression getImportReference() {
|
||||
public PyReferenceExpression getImportReferenceExpression() {
|
||||
final ASTNode node = getNode().findChildByType(PyElementTypes.REFERENCE_EXPRESSION);
|
||||
return node == null ? null : (PyReferenceExpression) node.getPsi();
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class PyImportElementImpl extends PyBaseElementImpl<PyImportElementStub>
|
||||
if (stub != null) {
|
||||
return stub.getImportedQName();
|
||||
}
|
||||
final PyReferenceExpression importReference = getImportReference();
|
||||
final PyReferenceExpression importReference = getImportReferenceExpression();
|
||||
return importReference != null ? importReference.asQualifiedName() : null;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public class PyImportElementImpl extends PyBaseElementImpl<PyImportElementStub>
|
||||
if (place.getContainingFile() != getContainingFile()) {
|
||||
return true;
|
||||
}
|
||||
final PyReferenceExpression importRef = getImportReference();
|
||||
final PyReferenceExpression importRef = getImportReferenceExpression();
|
||||
if (importRef != null) {
|
||||
final PsiElement element = importRef.getReference().resolve();
|
||||
if (element != null) {
|
||||
@@ -136,7 +136,7 @@ public class PyImportElementImpl extends PyBaseElementImpl<PyImportElementStub>
|
||||
|
||||
@NotNull
|
||||
private String getRefName(String default_name) {
|
||||
PyReferenceExpression ref = getImportReference();
|
||||
PyReferenceExpression ref = getImportReferenceExpression();
|
||||
if (ref != null) {
|
||||
String refname = ref.getName();
|
||||
if (refname != null) return refname;
|
||||
@@ -156,7 +156,7 @@ public class PyImportElementImpl extends PyBaseElementImpl<PyImportElementStub>
|
||||
buf.append("from ");
|
||||
PyReferenceExpression imp_src = ((PyFromImportStatement)elt).getImportSource();
|
||||
if (imp_src != null) {
|
||||
buf.append(PyResolveUtil.toPath(imp_src, "."));
|
||||
buf.append(PyResolveUtil.toPath(imp_src));
|
||||
}
|
||||
else {
|
||||
buf.append("<?>");
|
||||
@@ -189,7 +189,7 @@ public class PyImportElementImpl extends PyBaseElementImpl<PyImportElementStub>
|
||||
public Iterable<PyElement> iterateNames() {
|
||||
PyElement ret = getAsNameElement();
|
||||
if (ret == null) {
|
||||
List<PyExpression> unwound_path = PyResolveUtil.unwindQualifiers(getImportReference());
|
||||
List<PyExpression> unwound_path = PyResolveUtil.unwindQualifiers(getImportReferenceExpression());
|
||||
if ((unwound_path != null) && (unwound_path.size() > 0)) ret = unwound_path.get(0);
|
||||
}
|
||||
if (ret == null) {
|
||||
|
||||
+36
-14
@@ -29,14 +29,24 @@ import java.util.*;
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyImportReferenceImpl extends PyReferenceImpl {
|
||||
public class PyImportReference extends PyReferenceImpl {
|
||||
private final PyReferenceExpressionImpl myElement;
|
||||
|
||||
public PyImportReferenceImpl(PyReferenceExpressionImpl element, PyResolveContext context) {
|
||||
public PyImportReference(PyReferenceExpressionImpl element, PyResolveContext context) {
|
||||
super(element, context);
|
||||
myElement = element;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getUnresolvedDescription() {
|
||||
final PyImportStatement importStatement = PsiTreeUtil.getParentOfType(myElement, PyImportStatement.class);
|
||||
if (importStatement != null) {
|
||||
return "No module named " + myElement.getReferencedName();
|
||||
}
|
||||
return super.getUnresolvedDescription();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<RatedResolveResult> resolveInner() {
|
||||
@@ -44,21 +54,37 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
|
||||
final String referencedName = myElement.getReferencedName();
|
||||
if (referencedName == null) return ret;
|
||||
|
||||
int default_submodule_rate = RatedResolveResult.RATE_HIGH;
|
||||
int defaultSubmoduleRate = RatedResolveResult.RATE_HIGH;
|
||||
|
||||
// names inside module take precedence over submodules
|
||||
final PyImportElement import_elt = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class);
|
||||
if (import_elt != null) {
|
||||
if (ret.poke(ResolveImportUtil.findImportedNameInsideModule(import_elt, referencedName), RatedResolveResult.RATE_HIGH)) {
|
||||
default_submodule_rate = RatedResolveResult.RATE_NORMAL;
|
||||
defaultSubmoduleRate = RatedResolveResult.RATE_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
List<PsiElement> targets = ResolveImportUtil.resolveImportReference(myElement);
|
||||
final PyElement parent = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class, PyFromImportStatement.class); //importRef.getParent();
|
||||
List<PsiElement> targets;
|
||||
final PyQualifiedName qname = myElement.asQualifiedName();
|
||||
if (parent instanceof PyImportElement) {
|
||||
targets = ResolveImportUtil.multiResolveImportElement((PyImportElement)parent, qname);
|
||||
}
|
||||
else if (parent instanceof PyFromImportStatement) { // "from foo import"
|
||||
targets = ResolveImportUtil.resolveFromOrForeignImport((PyFromImportStatement)parent, qname);
|
||||
}
|
||||
else {
|
||||
return ret;
|
||||
}
|
||||
addRatedResults(ret, defaultSubmoduleRate, targets);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static void addRatedResults(ResolveResultList ret, int defaultSubmoduleRate, List<PsiElement> targets) {
|
||||
for (PsiElement target : targets) {
|
||||
target = PyUtil.turnDirIntoInit(target);
|
||||
if (target != null) { // ignore dirs without __init__.py, worthless
|
||||
int rate = default_submodule_rate;
|
||||
int rate = defaultSubmoduleRate;
|
||||
if (target instanceof PyFile) {
|
||||
VirtualFile vFile = ((PyFile)target).getVirtualFile();
|
||||
if (vFile != null && vFile.getLength() == 0) {
|
||||
@@ -68,8 +94,6 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
|
||||
ret.poke(target, rate);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -250,17 +274,15 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
|
||||
}
|
||||
|
||||
private void fillFromQName(PyQualifiedName thisQName, InsertHandler<LookupElement> insertHandler) {
|
||||
final List<PsiElement> dirs = ResolveImportUtil.resolveModulesInRoots(thisQName, myCurrentFile);
|
||||
for (PsiElement dir : dirs) {
|
||||
if (dir instanceof PsiDirectory) {
|
||||
fillFromDir((PsiDirectory)dir, insertHandler);
|
||||
}
|
||||
QualifiedNameResolver visitor = new QualifiedNameResolver(thisQName).fromElement(myCurrentFile);
|
||||
for (PsiDirectory dir : visitor.resultsOfType(PsiDirectory.class)) {
|
||||
fillFromDir(dir, insertHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void addImportedNames(@NotNull PyImportElement[] import_elts) {
|
||||
for (PyImportElement ielt : import_elts) {
|
||||
PyReferenceExpression ref = ielt.getImportReference();
|
||||
PyReferenceExpression ref = ielt.getImportReferenceExpression();
|
||||
if (ref != null) {
|
||||
String s = ref.getReferencedName();
|
||||
if (s != null) myNamesAlready.add(s);
|
||||
@@ -3,7 +3,6 @@ package com.jetbrains.python.psi.impl;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.PyImportElement;
|
||||
import com.jetbrains.python.psi.PyImportStatement;
|
||||
import com.jetbrains.python.psi.PyImportStatementBase;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -21,7 +20,7 @@ public class PyImportStatementNavigator {
|
||||
return null;
|
||||
}
|
||||
for (PyImportElement importElement : statement.getImportElements()) {
|
||||
if (element == importElement || element == importElement.getImportReference()){
|
||||
if (element == importElement || element == importElement.getImportReferenceExpression()){
|
||||
return statement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -157,12 +156,12 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParame
|
||||
final PyParameter[] params = parameterList.getParameters();
|
||||
PyFunction func = parameterList.getContainingFunction();
|
||||
if (func != null) {
|
||||
final Set<PyFunction.Flag> flags = PyUtil.detectDecorationsAndWrappersOf(func);
|
||||
if (params [0] == this && !flags.contains(PyFunction.Flag.STATICMETHOD)) {
|
||||
final PyFunction.Modifier modifier = func.getModifier();
|
||||
if (params [0] == this && modifier != PyFunction.Modifier.STATICMETHOD) {
|
||||
// must be 'self' or 'cls'
|
||||
final PyClass containingClass = func.getContainingClass();
|
||||
if (containingClass != null) {
|
||||
return new PyClassType(containingClass, flags.contains(PyFunction.Flag.CLASSMETHOD));
|
||||
return new PyClassType(containingClass, modifier == PyFunction.Modifier.CLASSMETHOD);
|
||||
}
|
||||
}
|
||||
if (isKeywordContainer()) {
|
||||
|
||||
+5
-5
@@ -36,8 +36,8 @@ import java.util.*;
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyQualifiedReferenceImpl extends PyReferenceImpl {
|
||||
public PyQualifiedReferenceImpl(PyQualifiedExpression element, PyResolveContext context) {
|
||||
public class PyQualifiedReference extends PyReferenceImpl {
|
||||
public PyQualifiedReference(PyQualifiedExpression element, PyResolveContext context) {
|
||||
super(element, context);
|
||||
}
|
||||
|
||||
@@ -344,9 +344,9 @@ public class PyQualifiedReferenceImpl extends PyReferenceImpl {
|
||||
}
|
||||
|
||||
private static Collection<PyExpression> collectAssignedAttributes(PyQualifiedExpression qualifier) {
|
||||
List<String> qualifier_path = PyResolveUtil.unwindQualifiersAsStrList(qualifier);
|
||||
if (qualifier_path != null) {
|
||||
AssignmentCollectProcessor proc = new AssignmentCollectProcessor(qualifier_path);
|
||||
PyQualifiedName qualifierPath = PyQualifiedName.fromReferenceChain(PyResolveUtil.unwindQualifiers(qualifier));
|
||||
if (qualifierPath != null) {
|
||||
AssignmentCollectProcessor proc = new AssignmentCollectProcessor(qualifierPath);
|
||||
PyResolveUtil.treeCrawlUp(proc, qualifier);
|
||||
return proc.getResult();
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
}
|
||||
}
|
||||
if (PsiTreeUtil.getParentOfType(this, PyImportElement.class, PyFromImportStatement.class) != null) {
|
||||
return new PyImportReferenceImpl(this, context);
|
||||
return new PyImportReference(this, context);
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
@@ -74,7 +74,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
}
|
||||
|
||||
if (qualifier != null) {
|
||||
return new PyQualifiedReferenceImpl(this, context);
|
||||
return new PyQualifiedReference(this, context);
|
||||
}
|
||||
|
||||
return new PyReferenceImpl(this, context);
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
@@ -29,9 +28,9 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
|
||||
@NotNull
|
||||
public Iterable<PyElement> iterateNames() {
|
||||
if (getParent() instanceof PyFromImportStatement) {
|
||||
PyFromImportStatement import_from_stmt = (PyFromImportStatement)getParent();
|
||||
PyReferenceExpression from_src = import_from_stmt.getImportSource();
|
||||
final List<PsiElement> importedFiles = ResolveImportUtil.resolveImportReference(from_src);
|
||||
PyFromImportStatement fromImportStatement = (PyFromImportStatement)getParent();
|
||||
final List<PsiElement> importedFiles = ResolveImportUtil.resolveFromOrForeignImport(fromImportStatement,
|
||||
fromImportStatement.getImportSourceQName());
|
||||
ChainIterable<PyElement> chain = new ChainIterable<PyElement>();
|
||||
for (PsiElement importedFile : new HashSet<PsiElement>(importedFiles)) { // resolver gives lots of duplicates
|
||||
final PsiElement source = PyUtil.turnDirIntoInit(importedFile);
|
||||
@@ -50,9 +49,9 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
|
||||
return null;
|
||||
}
|
||||
if (getParent() instanceof PyFromImportStatement) {
|
||||
PyFromImportStatement import_from_stmt = (PyFromImportStatement)getParent();
|
||||
PyReferenceExpression from_src = import_from_stmt.getImportSource();
|
||||
final List<PsiElement> importedFiles = ResolveImportUtil.resolveImportReference(from_src);
|
||||
PyFromImportStatement fromImportStatement = (PyFromImportStatement)getParent();
|
||||
final List<PsiElement> importedFiles = ResolveImportUtil.resolveFromOrForeignImport(fromImportStatement,
|
||||
fromImportStatement.getImportSourceQName());
|
||||
for (PsiElement importedFile : new HashSet<PsiElement>(importedFiles)) { // resolver gives lots of duplicates
|
||||
final PsiElement source = PyUtil.turnDirIntoInit(importedFile);
|
||||
if (source instanceof PyFile) {
|
||||
@@ -80,11 +79,11 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
|
||||
return new ItemPresentation() {
|
||||
|
||||
private String getName() {
|
||||
PyElement elt = PsiTreeUtil.getParentOfType(PyStarImportElementImpl.this, PyFromImportStatement.class);
|
||||
if (elt instanceof PyFromImportStatement) { // always? who knows :)
|
||||
PyReferenceExpression imp_src = ((PyFromImportStatement)elt).getImportSource();
|
||||
PyFromImportStatement elt = PsiTreeUtil.getParentOfType(PyStarImportElementImpl.this, PyFromImportStatement.class);
|
||||
if (elt != null) { // always? who knows :)
|
||||
PyReferenceExpression imp_src = elt.getImportSource();
|
||||
if (imp_src != null) {
|
||||
return PyResolveUtil.toPath(imp_src, ".");
|
||||
return PyResolveUtil.toPath(imp_src);
|
||||
}
|
||||
}
|
||||
return "<?>";
|
||||
@@ -95,7 +94,7 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
|
||||
}
|
||||
|
||||
public String getLocationString() {
|
||||
StringBuffer buf = new StringBuffer("| ");
|
||||
StringBuilder buf = new StringBuilder("| ");
|
||||
buf.append("from ").append(getName()).append(" import *");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
|
||||
@Override
|
||||
public PsiReference getReference() {
|
||||
if (getQualifier() != null) {
|
||||
return new PyQualifiedReferenceImpl(this, PyResolveContext.defaultContext());
|
||||
return new PyQualifiedReference(this, PyResolveContext.defaultContext());
|
||||
}
|
||||
return new PyTargetReference(this, PyResolveContext.defaultContext());
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.jetbrains.python.psi.PyAssignmentStatement;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.psi.impl.PyQualifiedName;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
@@ -16,9 +17,9 @@ public class AssignmentCollectProcessor implements PsiScopeProcessor {
|
||||
* Collects all assignments in context above given element, if they match given naming pattern.
|
||||
* Used to track creation of attributes by assignment (e.g in constructor).
|
||||
*/
|
||||
List<String> my_qualifier;
|
||||
List<PyExpression> my_result;
|
||||
Set<String> my_seen_names;
|
||||
private final PyQualifiedName myQualifier;
|
||||
private final List<PyExpression> myResult;
|
||||
private final Set<String> mySeenNames;
|
||||
|
||||
/**
|
||||
* Creates an instance to collect assignments of attributes to the object identified by 'qualifier'.
|
||||
@@ -28,11 +29,11 @@ public class AssignmentCollectProcessor implements PsiScopeProcessor {
|
||||
*
|
||||
* @param qualifier qualifying names, outermost first; must not be empty.
|
||||
*/
|
||||
public AssignmentCollectProcessor(@NotNull List<String> qualifier) {
|
||||
assert qualifier.size() > 0;
|
||||
my_qualifier = qualifier;
|
||||
my_result = new ArrayList<PyExpression>();
|
||||
my_seen_names = new HashSet<String>();
|
||||
public AssignmentCollectProcessor(@NotNull PyQualifiedName qualifier) {
|
||||
assert qualifier.getComponentCount() > 0;
|
||||
myQualifier = qualifier;
|
||||
myResult = new ArrayList<PyExpression>();
|
||||
mySeenNames = new HashSet<String>();
|
||||
}
|
||||
|
||||
public boolean execute(final PsiElement element, final ResolveState state) {
|
||||
@@ -41,19 +42,19 @@ public class AssignmentCollectProcessor implements PsiScopeProcessor {
|
||||
for (PyExpression ex : assignment.getTargets()) {
|
||||
if (ex instanceof PyTargetExpression) {
|
||||
final PyTargetExpression target = (PyTargetExpression)ex;
|
||||
List<String> quals = PyResolveUtil.unwindQualifiersAsStrList(target);
|
||||
List<PyExpression> qualsExpr = PyResolveUtil.unwindQualifiers(target);
|
||||
if (quals != null) {
|
||||
if (quals.size() == my_qualifier.size() + 1 && PyResolveUtil.pathsMatchStr(quals, my_qualifier)) {
|
||||
PyQualifiedName qualifiedName = PyQualifiedName.fromReferenceChain(qualsExpr);
|
||||
if (qualifiedName != null) {
|
||||
if (qualifiedName.getComponentCount() == myQualifier.getComponentCount() + 1 && qualifiedName.matchesPrefix(myQualifier)) {
|
||||
// a new attribute follows last qualifier; collect it.
|
||||
PyExpression last_elt = qualsExpr.get(qualsExpr.size() - 1); // last item is the outermost, new, attribute.
|
||||
String last_elt_name = last_elt.getName();
|
||||
if (!my_seen_names.contains(last_elt_name)) { // no dupes, only remember the latest
|
||||
my_result.add(last_elt);
|
||||
my_seen_names.add(last_elt_name);
|
||||
if (!mySeenNames.contains(last_elt_name)) { // no dupes, only remember the latest
|
||||
myResult.add(last_elt);
|
||||
mySeenNames.add(last_elt_name);
|
||||
}
|
||||
}
|
||||
else if (quals.size() < my_qualifier.size() + 1 && PyResolveUtil.pathsMatchStr(my_qualifier, quals)) {
|
||||
else if (qualifiedName.getComponentCount() < myQualifier.getComponentCount() + 1 && myQualifier.matchesPrefix(qualifiedName)) {
|
||||
// qualifier(s) get redefined; collect no more.
|
||||
return false;
|
||||
}
|
||||
@@ -66,12 +67,12 @@ public class AssignmentCollectProcessor implements PsiScopeProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a collection of exressions (parts of assignment expressions) where new attributes were defined. E.g. for "a.b.c = 1",
|
||||
* @return a collection of expressions (parts of assignment expressions) where new attributes were defined. E.g. for "a.b.c = 1",
|
||||
* the expression for 'c' is in the result.
|
||||
*/
|
||||
@NotNull
|
||||
public Collection<PyExpression> getResult() {
|
||||
return my_result;
|
||||
return myResult;
|
||||
}
|
||||
|
||||
public <T> T getHint(final Key<T> hintKey) {
|
||||
|
||||
@@ -15,11 +15,11 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PythonDialectsTokenSetProvider;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyQualifiedName;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -232,57 +232,13 @@ public class PyResolveUtil {
|
||||
return treeCrawlUp(processor, false, elt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to match two [qualified] reference expression paths by names; target must be a 'sublist' of source to match.
|
||||
* E.g., 'a.b.c.d' and 'a.b.c' would match, while 'a.b.c' and 'a.b.c.d' would not. Eqaully, 'a.b.c' and 'a.b.d' would not match.
|
||||
* If either source or target is null, false is returned.
|
||||
*
|
||||
* @param source_path expression path to match (the longer list of qualifiers).
|
||||
* @param target_path expression path to match against (hopeful sublist of qualifiers of source).
|
||||
* @return true if source matches target.
|
||||
* @see #unwindQualifiers(PyQualifiedExpression) .
|
||||
*/
|
||||
public static <S extends PyExpression, T extends PyExpression> boolean pathsMatch(List<S> source_path, List<T> target_path) {
|
||||
// turn qualifiers into lists
|
||||
if ((source_path == null) || (target_path == null)) return false;
|
||||
// compare until target is exhausted
|
||||
Iterator<S> source_iter = source_path.iterator();
|
||||
for (final T target_elt : target_path) {
|
||||
if (source_iter.hasNext()) {
|
||||
S source_elt = source_iter.next();
|
||||
if (!target_elt.getText().equals(source_elt.getText())) return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
} // source exhausted before target
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean pathsMatchStr(List<String> source_path, List<String> target_path) {
|
||||
// turn qualifiers into lists
|
||||
if ((source_path == null) || (target_path == null)) return false;
|
||||
// compare until target is exhausted
|
||||
Iterator<String> source_iter = source_path.iterator();
|
||||
for (final String target_elt : target_path) {
|
||||
if (source_iter.hasNext()) {
|
||||
String source_elt = source_iter.next();
|
||||
if (!target_elt.equals(source_elt)) return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
} // source exhausted before target
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwinds a multi-level qualified expression into a path, as seen in source text, i.e. outermost qualifier first.
|
||||
*
|
||||
* @param expr an experssion to unwind.
|
||||
* @param expr an expression to unwind.
|
||||
* @return path as a list of ref expressions.
|
||||
*/
|
||||
@NotNull
|
||||
public static List<PyExpression> unwindQualifiers(final PyQualifiedExpression expr) {
|
||||
final List<PyExpression> path = new LinkedList<PyExpression>();
|
||||
PyQualifiedExpression e = expr;
|
||||
@@ -305,27 +261,18 @@ public class PyResolveUtil {
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String toPath(PyQualifiedExpression expr, String separator) {
|
||||
public static String toPath(PyQualifiedExpression expr) {
|
||||
if (expr == null) return "";
|
||||
List<PyExpression> path = unwindQualifiers(expr);
|
||||
if (path != null) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
boolean is_not_first = false;
|
||||
for (PyExpression ex : path) {
|
||||
if (is_not_first) {
|
||||
buf.append(separator);
|
||||
}
|
||||
else {
|
||||
is_not_first = true;
|
||||
}
|
||||
buf.append(ex.getName());
|
||||
}
|
||||
return buf.toString();
|
||||
final PyQualifiedName qName = PyQualifiedName.fromReferenceChain(path);
|
||||
if (qName != null) {
|
||||
return qName.toString();
|
||||
}
|
||||
else {
|
||||
String s = expr.getName();
|
||||
return s != null ? s : "";
|
||||
String name = expr.getName();
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
package com.jetbrains.python.psi.resolve;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.facet.FacetManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.jetbrains.django.facet.DjangoFacetType;
|
||||
import com.jetbrains.python.console.PydevConsoleRunner;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyQualifiedName;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Resolves the specified qualified name in the specified context (module, all modules or a file) to a file or directory.
|
||||
*
|
||||
* @author yole
|
||||
*/
|
||||
public class QualifiedNameResolver implements RootVisitor {
|
||||
boolean myCheckForPackage = true;
|
||||
@Nullable private Module myModule;
|
||||
private PsiFile myFootholdFile;
|
||||
private final @NotNull PyQualifiedName myQualifiedName;
|
||||
@NotNull PsiManager myPsiManager;
|
||||
final Set<PsiElement> results = Sets.newLinkedHashSet();
|
||||
private boolean myAcceptRootAsTopLevelPackage;
|
||||
private boolean myVisitAllModules = false;
|
||||
private int myRelativeLevel = -1;
|
||||
private boolean myWithoutRoots;
|
||||
private Sdk myWithSdk;
|
||||
|
||||
public QualifiedNameResolver(@NotNull String qNameString) {
|
||||
myQualifiedName = PyQualifiedName.fromDottedString(qNameString);
|
||||
}
|
||||
|
||||
public QualifiedNameResolver(@NotNull PyQualifiedName qName) {
|
||||
myQualifiedName = qName;
|
||||
}
|
||||
|
||||
public QualifiedNameResolver fromElement(@NotNull PsiElement foothold) {
|
||||
myFootholdFile = foothold.getContainingFile().getOriginalFile();
|
||||
myPsiManager = PsiManager.getInstance(foothold.getProject());
|
||||
setModule(ModuleUtil.findModuleForPsiElement(myFootholdFile));
|
||||
if (PydevConsoleRunner.isInPydevConsole(foothold)) {
|
||||
withAllModules();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QualifiedNameResolver fromModule(@NotNull Module module) {
|
||||
setModule(module);
|
||||
myPsiManager = PsiManager.getInstance(module.getProject());
|
||||
return this;
|
||||
}
|
||||
|
||||
private void setModule(@Nullable Module module) {
|
||||
myModule = module;
|
||||
if (module != null && FacetManager.getInstance(module).getFacetByType(DjangoFacetType.ID) != null) {
|
||||
myAcceptRootAsTopLevelPackage = true;
|
||||
}
|
||||
}
|
||||
|
||||
public QualifiedNameResolver withAllModules() {
|
||||
myVisitAllModules = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that we need to look for the name in the specified SDK (instead of the SDK assigned to the module, if any).
|
||||
*
|
||||
* @param sdk the SDK in which the name should be searched.
|
||||
* @return this
|
||||
*/
|
||||
public QualifiedNameResolver withSdk(Sdk sdk) {
|
||||
myWithSdk = sdk;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether we should attempt to resolve imports relative to the current file.
|
||||
*
|
||||
* @param relativeLevel if >= 0, we try to resolve at the specified number of levels above the current file.
|
||||
* @return this
|
||||
*/
|
||||
public QualifiedNameResolver withRelative(int relativeLevel) {
|
||||
myRelativeLevel = relativeLevel;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that we should only try to resolve relative to the current file, not in roots.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public QualifiedNameResolver withoutRoots() {
|
||||
myWithoutRoots = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies that we're looking for a file in a directory hierarchy, not a module in the Python package hierarchy
|
||||
* (so we don't need to check for existence of __init__.py)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public QualifiedNameResolver withPlainDirectories() {
|
||||
myCheckForPackage = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean visitRoot(final VirtualFile root) {
|
||||
if (!root.isValid()) {
|
||||
return true;
|
||||
}
|
||||
PsiElement module = resolveInRoot(root);
|
||||
if (module != null) {
|
||||
results.add(module);
|
||||
}
|
||||
|
||||
if (myAcceptRootAsTopLevelPackage && myQualifiedName.matchesPrefix(PyQualifiedName.fromDottedString(root.getName()))) {
|
||||
module = resolveInRoot(root.getParent());
|
||||
if (module != null) {
|
||||
results.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<PsiElement> resultsAsList() {
|
||||
if (myFootholdFile != null && !myFootholdFile.isValid()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (myRelativeLevel >= 0) {
|
||||
assert myFootholdFile != null;
|
||||
PsiDirectory dir = myFootholdFile.getContainingDirectory();
|
||||
if (myRelativeLevel > 0) {
|
||||
dir = ResolveImportUtil.stepBackFrom(myFootholdFile, myRelativeLevel);
|
||||
|
||||
}
|
||||
PsiElement module = resolveModuleAt(dir, null);
|
||||
if (module != null) {
|
||||
results.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
if (!myWithoutRoots) {
|
||||
PythonPathCache cache = findMyCache();
|
||||
if (cache != null) {
|
||||
final List<PsiElement> cachedResults = cache.get(myQualifiedName);
|
||||
if (cachedResults != null) {
|
||||
return cachedResults;
|
||||
}
|
||||
}
|
||||
|
||||
if (myVisitAllModules) {
|
||||
for (Module mod : ModuleManager.getInstance(myPsiManager.getProject()).getModules()) {
|
||||
RootVisitorHost.visitRoots(mod, false, this);
|
||||
}
|
||||
}
|
||||
else if (myModule != null) {
|
||||
final boolean otherSdk = withOtherSdk();
|
||||
RootVisitorHost.visitRoots(myModule, otherSdk, this);
|
||||
if (otherSdk) {
|
||||
RootVisitorHost.visitSdkRoots(myWithSdk, this);
|
||||
}
|
||||
}
|
||||
else if (myFootholdFile != null) {
|
||||
RootVisitorHost.visitSdkRoots(myFootholdFile, this);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
final ArrayList<PsiElement> resultList = Lists.newArrayList(results);
|
||||
if (cache != null) {
|
||||
cache.put(myQualifiedName, resultList);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
return Lists.newArrayList(results);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement firstResult() {
|
||||
final List<PsiElement> results = resultsAsList();
|
||||
return results.size() > 0 ? results.get(0) : null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public <T extends PsiElement> List<T> resultsOfType(Class<T> clazz) {
|
||||
List<T> result = new ArrayList<T>();
|
||||
for (PsiElement element : resultsAsList()) {
|
||||
if (clazz.isInstance(element)) {
|
||||
//noinspection unchecked
|
||||
result.add((T) element);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends PsiElement> T firstResultOfType(Class<T> clazz) {
|
||||
final List<T> list = resultsOfType(clazz);
|
||||
return list.size() > 0 ? list.get(0) : null;
|
||||
}
|
||||
|
||||
private boolean withOtherSdk() {
|
||||
return myWithSdk != null && myWithSdk != PythonSdkType.findPythonSdk(myModule);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PythonPathCache findMyCache() {
|
||||
if (myVisitAllModules) {
|
||||
return null;
|
||||
}
|
||||
if (myModule != null) {
|
||||
return withOtherSdk() ? null : PythonModulePathCache.getInstance(myModule);
|
||||
}
|
||||
if (myFootholdFile != null) {
|
||||
final Sdk sdk = PyBuiltinCache.findSdkForFile(myFootholdFile);
|
||||
if (sdk != null) {
|
||||
return PythonSdkPathCache.getInstance(myPsiManager.getProject(), sdk);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement resolveInRoot(VirtualFile root) {
|
||||
if (!root.isDirectory()) {
|
||||
// if we have added a file as a root, it's unlikely that we'll be able to resolve anything under it in 'files only' resolve mode
|
||||
return null;
|
||||
}
|
||||
return resolveModuleAt(myPsiManager.findDirectory(root), root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a module at given directory, unwinding qualifiers and traversing directories as needed.
|
||||
*
|
||||
* @param directory where to start from; top qualifier will be searched for here.
|
||||
* @param root an SDK, library or content root from which we're searching, or null if we're searching relatively
|
||||
* @return module's file, or null.
|
||||
*/
|
||||
@Nullable
|
||||
private PsiElement resolveModuleAt(@Nullable PsiDirectory directory, @Nullable VirtualFile root) {
|
||||
// prerequisites
|
||||
if (directory == null || !directory.isValid()) return null;
|
||||
|
||||
PsiElement seeker = directory;
|
||||
for (String name : myQualifiedName.getComponents()) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
seeker = ResolveImportUtil.resolveChild(seeker, name, myFootholdFile, root, true, myCheckForPackage);
|
||||
}
|
||||
return seeker;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
package com.jetbrains.python.psi.resolve;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.facet.FacetManager;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.*;
|
||||
@@ -15,11 +11,8 @@ import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.django.facet.DjangoFacetType;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.console.PydevConsoleRunner;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyFileImpl;
|
||||
@@ -29,7 +22,10 @@ import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.jetbrains.python.psi.FutureFeature.ABSOLUTE_IMPORT;
|
||||
|
||||
@@ -113,7 +109,7 @@ public class ResolveImportUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiElement> multiResolveImportElement(PyImportElement import_element, final PyQualifiedName qName) {
|
||||
public static List<PsiElement> multiResolveImportElement(PyImportElement import_element, final PyQualifiedName qName) {
|
||||
if (qName == null) return Collections.emptyList();
|
||||
|
||||
// TODO: search for entire names, not for first component only!
|
||||
@@ -160,28 +156,6 @@ public class ResolveImportUtil {
|
||||
return result != null ? Collections.singletonList(result) : Collections.<PsiElement>emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<PsiElement> resolveImportReference(final PyReferenceExpression importRef) {
|
||||
// prerequisites
|
||||
if (importRef == null) return Collections.emptyList();
|
||||
if (!importRef.isValid()) return Collections.emptyList(); // we often catch a reparse while in a process of resolution
|
||||
final String referencedName = importRef.getReferencedName(); // it will be the "foo" in later comments
|
||||
if (referencedName == null) return Collections.emptyList();
|
||||
final PsiFile file = importRef.getContainingFile();
|
||||
if (file == null || !file.isValid()) return Collections.emptyList();
|
||||
|
||||
final PyElement parent = PsiTreeUtil.getParentOfType(importRef, PyImportElement.class, PyFromImportStatement.class); //importRef.getParent();
|
||||
final PyQualifiedName qname = importRef.asQualifiedName();
|
||||
if (parent instanceof PyImportElement) {
|
||||
PyImportElement import_element = (PyImportElement)parent;
|
||||
return multiResolveImportElement(import_element, qname);
|
||||
}
|
||||
else if (parent instanceof PyFromImportStatement) { // "from foo import"
|
||||
return resolveFromOrForeignImport((PyFromImportStatement)parent, qname);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static List<PsiElement> resolveFromOrForeignImport(PyFromImportStatement fromImportStatement, PyQualifiedName qname) {
|
||||
final List<PsiElement> results = resolveFromImportStatementSource(fromImportStatement, qname);
|
||||
if (results.isEmpty() && qname != null && qname.getComponentCount() > 0) {
|
||||
@@ -215,152 +189,41 @@ public class ResolveImportUtil {
|
||||
*
|
||||
* @param qualifiedName qualified name of the module reference to resolve
|
||||
* @param source_file where that reference resides; serves as PSI foothold to determine module, project, etc.
|
||||
* @param import_is_absolute if false, try old python 2.x's "relative first, absolute next" approach.
|
||||
* @param relative_level if > 0, step back from source_file and resolve from there (even if import_is_absolute is false!).
|
||||
* @param importIsAbsolute if false, try old python 2.x's "relative first, absolute next" approach.
|
||||
* @param relativeLevel if > 0, step back from source_file and resolve from there (even if importIsAbsolute is false!).
|
||||
* @return list of possible candidates
|
||||
*/
|
||||
@NotNull
|
||||
public static List<PsiElement> resolveModule(@Nullable PyQualifiedName qualifiedName, PsiFile source_file,
|
||||
boolean import_is_absolute, int relative_level) {
|
||||
boolean importIsAbsolute, int relativeLevel) {
|
||||
if (qualifiedName == null) return Collections.emptyList();
|
||||
String marker = StringUtil.join(qualifiedName.getComponents(), ".") + "#" + Integer.toString(relative_level);
|
||||
String marker = StringUtil.join(qualifiedName.getComponents(), ".") + "#" + Integer.toString(relativeLevel);
|
||||
Set<String> being_imported = ourBeingImported.get();
|
||||
if (being_imported.contains(marker)) return Collections.emptyList(); // break endless loop in import
|
||||
try {
|
||||
being_imported.add(marker);
|
||||
if (relative_level > 0) {
|
||||
QualifiedNameResolver visitor = new QualifiedNameResolver(qualifiedName).fromElement(source_file);
|
||||
if (relativeLevel > 0) {
|
||||
// "from ...module import"
|
||||
final PsiElement module = resolveModuleAt(stepBackFrom(source_file, relative_level), source_file, qualifiedName);
|
||||
return module != null ? Collections.singletonList(module) : Collections.<PsiElement>emptyList();
|
||||
visitor.withRelative(relativeLevel).withoutRoots();
|
||||
}
|
||||
else { // "from module import"
|
||||
if (import_is_absolute) {
|
||||
return resolveModulesInRoots(qualifiedName, source_file);
|
||||
}
|
||||
else {
|
||||
final PsiDirectory dir = source_file.getOriginalFile().getContainingDirectory();
|
||||
PsiElement module = resolveModuleAt(dir, source_file, qualifiedName);
|
||||
if (module != null) {
|
||||
return Collections.singletonList(module);
|
||||
}
|
||||
List<PsiElement> found_in_roots = resolveModulesInRoots(qualifiedName, source_file);
|
||||
if (found_in_roots.size() > 0) return found_in_roots;
|
||||
|
||||
return Collections.emptyList();
|
||||
if (!importIsAbsolute) {
|
||||
visitor.withRelative(0);
|
||||
}
|
||||
}
|
||||
return visitor.resultsAsList();
|
||||
}
|
||||
finally {
|
||||
being_imported.remove(marker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a module at given directory, unwinding qualifiers and traversing directories as needed.
|
||||
*
|
||||
* @param directory where to start from; top qualifier will be searched for here.
|
||||
* @param sourceFile the file containing the import statement being resolved
|
||||
* @param qualifiedName the qualified name of the module to search
|
||||
* @return module's file, or null.
|
||||
*/
|
||||
@Nullable
|
||||
private static PsiElement resolveModuleAt(PsiDirectory directory, PsiFile sourceFile, PyQualifiedName qualifiedName) {
|
||||
// prerequisites
|
||||
if (directory == null || !directory.isValid()) return null;
|
||||
if (sourceFile == null || !sourceFile.isValid()) return null;
|
||||
|
||||
PsiElement seeker = directory;
|
||||
for (String name : qualifiedName.getComponents()) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
seeker = resolveChild(seeker, name, sourceFile, null, true, true);
|
||||
}
|
||||
return seeker;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement resolveModuleInRoots(@NotNull PyQualifiedName moduleQualifiedName, @Nullable PsiElement foothold) {
|
||||
final List<PsiElement> candidates = resolveModulesInRoots(moduleQualifiedName, foothold);
|
||||
return candidates.isEmpty() ? null : candidates.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of directories/files under different project roots which match the specified qualified name.
|
||||
*
|
||||
* @param moduleQualifiedName the qualified name to find
|
||||
* @param foothold the PSI element in the context of which the search is performed
|
||||
* @return the list of matching directories or files, or an empty list if nothing was found
|
||||
*/
|
||||
@NotNull
|
||||
public static List<PsiElement> resolveModulesInRoots(@NotNull PyQualifiedName moduleQualifiedName, @Nullable PsiElement foothold) {
|
||||
if (foothold == null || !foothold.isValid()) return Collections.emptyList();
|
||||
PsiFile footholdFile = foothold.getContainingFile();
|
||||
if (footholdFile == null || !footholdFile.isValid()) return Collections.emptyList();
|
||||
|
||||
PythonPathCache cache = getPathCache(foothold);
|
||||
if (cache != null) {
|
||||
final List<PsiElement> cachedResults = cache.get(moduleQualifiedName);
|
||||
if (cachedResults != null) {
|
||||
return cachedResults;
|
||||
}
|
||||
}
|
||||
|
||||
final Module module = ModuleUtil.findModuleForPsiElement(foothold);
|
||||
|
||||
List<PsiElement> results;
|
||||
|
||||
if (PydevConsoleRunner.isInPydevConsole(foothold)) {
|
||||
results = visitRootsInAllModules(moduleQualifiedName, foothold, footholdFile);
|
||||
}
|
||||
else {
|
||||
results = visitRoots(moduleQualifiedName, foothold.getManager(), module, foothold, true);
|
||||
}
|
||||
|
||||
if (cache != null) {
|
||||
cache.put(moduleQualifiedName, results);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static List<PsiElement> visitRootsInAllModules(PyQualifiedName moduleQualifiedName, PsiElement foothold, PsiFile footholdFile) {
|
||||
Set<PsiElement> res = Sets.newHashSet();
|
||||
for (Module mod : ModuleManager.getInstance(footholdFile.getProject()).getModules()) {
|
||||
res.addAll(visitRoots(moduleQualifiedName, foothold.getManager(), mod, foothold, true));
|
||||
}
|
||||
return Lists.newArrayList(res);
|
||||
}
|
||||
|
||||
private static List<PsiElement> visitRoots(@NotNull PyQualifiedName moduleQualifiedName,
|
||||
@NotNull PsiManager manager,
|
||||
@Nullable Module module,
|
||||
@Nullable PsiElement foothold,
|
||||
boolean checkForPackage) {
|
||||
|
||||
PsiFile footholdFile = foothold != null ? foothold.getContainingFile() : null;
|
||||
boolean has_djando_facet = false;
|
||||
if (module != null) {
|
||||
has_djando_facet = FacetManager.getInstance(module).getFacetByType(DjangoFacetType.ID) != null;
|
||||
}
|
||||
ResolveInRootVisitor visitor;
|
||||
// resolve the name considering every source root as a package dir, as if it's a deployed package. django console does so.
|
||||
if (has_djando_facet) {
|
||||
visitor = new ResolveInRootAsTopPackageVisitor(moduleQualifiedName, manager, footholdFile, checkForPackage);
|
||||
}
|
||||
else {
|
||||
visitor = new ResolveInRootVisitor(moduleQualifiedName, manager, footholdFile, checkForPackage);
|
||||
}
|
||||
if (module != null) {
|
||||
visitRoots(module, visitor);
|
||||
return visitor.resultsAsList();
|
||||
}
|
||||
else if (foothold != null) {
|
||||
visitSdkRoots(foothold, visitor);
|
||||
return visitor.resultsAsList();
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (foothold == null) return null;
|
||||
QualifiedNameResolver visitor = new QualifiedNameResolver(moduleQualifiedName).fromElement(foothold);
|
||||
return visitor.firstResult();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -379,175 +242,6 @@ public class ResolveImportUtil {
|
||||
return cache;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<PsiElement> resolveModulesInRoots(@NotNull Module module, @NotNull PyQualifiedName moduleQualifiedName,
|
||||
boolean checkForPackage) {
|
||||
PythonPathCache cache = PythonModulePathCache.getInstance(module);
|
||||
final List<PsiElement> cachedResults = cache.get(moduleQualifiedName);
|
||||
if (cachedResults != null) {
|
||||
return cachedResults;
|
||||
}
|
||||
List<PsiElement> results = visitRoots(moduleQualifiedName, PsiManager.getInstance(module.getProject()), module, null, checkForPackage);
|
||||
cache.put(moduleQualifiedName, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<PsiElement> resolveModulesInRootProvider(@NotNull RootProvider rootProvider,
|
||||
@NotNull Module module,
|
||||
@NotNull PyQualifiedName moduleQualifiedName) {
|
||||
ResolveInRootVisitor visitor = new ResolveInRootVisitor(moduleQualifiedName, PsiManager.getInstance(module.getProject()), null,
|
||||
true);
|
||||
if (visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) {
|
||||
for (VirtualFile file : rootProvider.getFiles(OrderRootType.CLASSES)) {
|
||||
visitor.visitRoot(file);
|
||||
}
|
||||
}
|
||||
return visitor.resultsAsList();
|
||||
}
|
||||
|
||||
public static void visitRoots(@NotNull final PsiElement elt, @NotNull final RootVisitor visitor) {
|
||||
// real search
|
||||
final Module module = ModuleUtil.findModuleForPsiElement(elt);
|
||||
if (module != null) {
|
||||
visitRoots(module, visitor);
|
||||
}
|
||||
else {
|
||||
visitSdkRoots(elt, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
public static void visitRoots(@NotNull Module module, final RootVisitor visitor) {
|
||||
OrderEnumerator.orderEntries(module).recursively().forEach(new Processor<OrderEntry>() {
|
||||
@Override
|
||||
public boolean process(OrderEntry orderEntry) {
|
||||
if (orderEntry instanceof ModuleSourceOrderEntry) {
|
||||
return visitModuleContentEntries(((ModuleSourceOrderEntry)orderEntry).getRootModel(), visitor);
|
||||
}
|
||||
return visitOrderEntryRoots(visitor, orderEntry);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits module content, sdk roots and libraries
|
||||
*/
|
||||
public static void visitRoots(@NotNull Module module, @NotNull Sdk sdk, RootVisitor visitor) {
|
||||
if (!visitModuleContentEntries(ModuleRootManager.getInstance(module), visitor)) return;
|
||||
// else look in SDK roots
|
||||
if (visitSdkRoots(visitor, sdk)) return;
|
||||
|
||||
//look in libraries
|
||||
ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
|
||||
rootManager.orderEntries().process(new LibraryRootVisitingPolicy(visitor), null);
|
||||
}
|
||||
|
||||
private static void visitSdkRoots(PsiElement elt, RootVisitor visitor) {
|
||||
// no module, another way to look in SDK roots
|
||||
final PsiFile elt_psifile = elt.getContainingFile();
|
||||
if (elt_psifile != null) { // formality
|
||||
final VirtualFile elt_vfile = elt_psifile.getOriginalFile().getVirtualFile();
|
||||
List<OrderEntry> orderEntries = null;
|
||||
if (elt_vfile != null) { // reality
|
||||
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(elt.getProject()).getFileIndex();
|
||||
orderEntries = fileIndex.getOrderEntriesForFile(elt_vfile);
|
||||
if (orderEntries.size() > 0) {
|
||||
for (OrderEntry entry : orderEntries) {
|
||||
if (!visitOrderEntryRoots(visitor, entry)) break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
orderEntries = null;
|
||||
}
|
||||
}
|
||||
|
||||
// out-of-project file or non-file(e.g. console) - use roots of SDK assigned to project
|
||||
if (orderEntries == null) {
|
||||
final Sdk sdk = ProjectRootManager.getInstance(elt.getProject()).getProjectSdk();
|
||||
if (sdk != null) {
|
||||
visitSdkRoots(visitor, sdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean visitSdkRoots(@NotNull RootVisitor visitor, @NotNull Sdk sdk) {
|
||||
final VirtualFile[] roots = sdk.getRootProvider().getFiles(OrderRootType.CLASSES);
|
||||
for (VirtualFile root : roots) {
|
||||
if (!visitor.visitRoot(root)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static boolean visitModuleContentEntries(ModuleRootModel rootModel, RootVisitor visitor) {
|
||||
// look in module sources
|
||||
Set<VirtualFile> contentRoots = Sets.newHashSet();
|
||||
for (ContentEntry entry : rootModel.getContentEntries()) {
|
||||
VirtualFile rootFile = entry.getFile();
|
||||
|
||||
if (rootFile != null && !visitor.visitRoot(rootFile)) return false;
|
||||
contentRoots.add(rootFile);
|
||||
for (VirtualFile folder : entry.getSourceFolderFiles()) {
|
||||
if (!visitor.visitRoot(folder)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean visitOrderEntryRoots(RootVisitor visitor, OrderEntry entry) {
|
||||
Set<VirtualFile> allRoots = new LinkedHashSet<VirtualFile>();
|
||||
Collections.addAll(allRoots, entry.getFiles(OrderRootType.SOURCES));
|
||||
Collections.addAll(allRoots, entry.getFiles(OrderRootType.CLASSES));
|
||||
for (VirtualFile root : allRoots) {
|
||||
if (!visitor.visitRoot(root)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement resolveInRoots(@NotNull final PsiElement context, final String name) {
|
||||
return resolveInRoots(context, PyQualifiedName.fromDottedString(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for a name among element's module's roots; if there's no module, then among project's roots.
|
||||
*
|
||||
* @param context PSI element that defines the module and/or the project.
|
||||
* @param qualifiedName module name to be found among roots.
|
||||
* @return a PsiFile, a child of a root.
|
||||
*/
|
||||
@Nullable
|
||||
public static PsiElement resolveInRoots(@NotNull final PsiElement context, final PyQualifiedName qualifiedName) {
|
||||
// NOTE: a quick and dirty temporary fix for "current dir" root path, which is assumed to be present first (but may be not).
|
||||
if (qualifiedName.getComponentCount() == 1) {
|
||||
PsiElement res = resolveInCurrentDir(context, qualifiedName.getLastComponent());
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return resolveModuleInRoots(qualifiedName, context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement resolveInCurrentDir(@NotNull final PsiElement elt, final String refName) {
|
||||
PsiFile pfile = elt.getContainingFile();
|
||||
VirtualFile vfile = pfile.getVirtualFile();
|
||||
if (vfile == null) { // we're probably within a copy, e.g. for completion; get the real thing
|
||||
pfile = pfile.getOriginalFile();
|
||||
}
|
||||
PsiDirectory pdir = pfile.getContainingDirectory();
|
||||
if (pdir != null) {
|
||||
PsiElement child_elt = resolveChild(pdir, refName, pfile, null, true, true);
|
||||
if (child_elt != null) return child_elt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveForeignImport(@NotNull final PyElement importElement,
|
||||
@NotNull final PyQualifiedName importText,
|
||||
@@ -561,99 +255,6 @@ public class ResolveImportUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean findInRoots(Module module, Sdk pythonSdk, String name) {
|
||||
ResolveNameVisitor
|
||||
visitor = new ResolveNameVisitor(PsiManager.getInstance(module.getProject()), name);
|
||||
visitRoots(module, pythonSdk, visitor);
|
||||
return visitor.isFound();
|
||||
}
|
||||
|
||||
static class ResolveInRootVisitor implements RootVisitor {
|
||||
final PsiFile myFootholdFile;
|
||||
final boolean myCheckForPackage;
|
||||
final @NotNull PyQualifiedName myQualifiedName;
|
||||
final @NotNull PsiManager myPsiManager;
|
||||
final Set<PsiElement> results = Sets.newLinkedHashSet();
|
||||
|
||||
public ResolveInRootVisitor(@NotNull PyQualifiedName qName,
|
||||
@NotNull PsiManager psiManager,
|
||||
@Nullable PsiFile foothold_file,
|
||||
boolean checkForPackage) {
|
||||
myQualifiedName = qName;
|
||||
myPsiManager = psiManager;
|
||||
myFootholdFile = foothold_file;
|
||||
myCheckForPackage = checkForPackage;
|
||||
}
|
||||
|
||||
public boolean visitRoot(final VirtualFile root) {
|
||||
if (!root.isValid()) {
|
||||
return true;
|
||||
}
|
||||
PsiElement module = resolveInRoot(root, myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage);
|
||||
if (module != null) {
|
||||
results.add(module);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<PsiElement> resultsAsList() {
|
||||
return Lists.newArrayList(results);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static PsiElement resolveInRoot(VirtualFile root,
|
||||
PyQualifiedName qualifiedName,
|
||||
PsiManager psiManager,
|
||||
@Nullable PsiFile foothold_file,
|
||||
boolean checkForPackage) {
|
||||
PsiElement module = root.isDirectory() ? psiManager.findDirectory(root) : psiManager.findFile(root);
|
||||
if (module == null) return null;
|
||||
for (String component : qualifiedName.getComponents()) {
|
||||
if (component == null) {
|
||||
module = null;
|
||||
break;
|
||||
}
|
||||
module = resolveChild(module, component, foothold_file, root, false, checkForPackage); // only files, we want a module
|
||||
}
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits roots and detects if qName is a name of top package coincinding with a root:
|
||||
* that is, tha package is not one of root's children, but the root itself.
|
||||
*/
|
||||
private static class ResolveInRootAsTopPackageVisitor extends ResolveInRootVisitor {
|
||||
public ResolveInRootAsTopPackageVisitor(@NotNull PyQualifiedName qName,
|
||||
@NotNull PsiManager psiManager,
|
||||
@Nullable PsiFile foothold_file,
|
||||
boolean checkForPackage) {
|
||||
super(qName, psiManager, foothold_file, checkForPackage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visitRoot(VirtualFile root) {
|
||||
if (!root.isValid()) {
|
||||
return true;
|
||||
}
|
||||
PsiElement module = resolveInRoot(root, myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage);
|
||||
if (module != null) {
|
||||
results.add(module);
|
||||
}
|
||||
|
||||
if (myQualifiedName.matchesPrefix(PyQualifiedName.fromDottedString(root.getName()))) {
|
||||
module = resolveInRoot(root.getParent(), myQualifiedName, myPsiManager, myFootholdFile, myCheckForPackage);
|
||||
if (module != null) {
|
||||
results.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find referencedName under the parent element. Used to resolve any names that look imported.
|
||||
* Parent might happen to be a PyFile(__init__.py), then it is treated <i>both</i> as a file and as ist base dir.
|
||||
@@ -685,7 +286,10 @@ public class ResolveImportUtil {
|
||||
|
||||
// OTOH, quite often a module named foo exports a class or function named foo, which is used as a fallback
|
||||
// by a module one level higher (e.g. curses.set_key). Prefer it to submodule if possible.
|
||||
ret = ((PyFileImpl)parent).getElementNamed(referencedName, false);
|
||||
PsiElement elementNamed = ((PyFileImpl)parent).getElementNamed(referencedName, false);
|
||||
if (!fileOnly || PyUtil.instanceOf(elementNamed, PsiFile.class, PsiDirectory.class)) {
|
||||
ret = elementNamed;
|
||||
}
|
||||
if (ret != null && !PyUtil.instanceOf(ret, PsiFile.class, PsiDirectory.class) &&
|
||||
PsiTreeUtil.getStubOrPsiParentOfType(ret, PyExceptPart.class) == null) {
|
||||
return ret;
|
||||
@@ -865,7 +469,7 @@ public class ResolveImportUtil {
|
||||
return name;
|
||||
}
|
||||
PathChoosingVisitor visitor = new PathChoosingVisitor(vfile);
|
||||
visitRoots(foothold, visitor);
|
||||
RootVisitorHost.visitRoots(foothold, visitor);
|
||||
final PyQualifiedName result = visitor.getResult();
|
||||
if (cache != null) {
|
||||
cache.putName(vfile, result);
|
||||
@@ -881,7 +485,7 @@ public class ResolveImportUtil {
|
||||
return name.toString();
|
||||
}
|
||||
PathChoosingVisitor visitor = new PathChoosingVisitor(vfile);
|
||||
visitRoots(module, visitor);
|
||||
RootVisitorHost.visitRoots(module, false, visitor);
|
||||
final PyQualifiedName result = visitor.getResult();
|
||||
cache.putName(vfile, result);
|
||||
return result == null ? null : result.toString();
|
||||
@@ -964,27 +568,6 @@ public class ResolveImportUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class LibraryRootVisitingPolicy extends RootPolicy<PsiElement> {
|
||||
private final RootVisitor myVisitor;
|
||||
|
||||
public LibraryRootVisitingPolicy(RootVisitor visitor) {
|
||||
myVisitor = visitor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement visitJdkOrderEntry(final JdkOrderEntry jdkOrderEntry, final PsiElement value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, PsiElement value) {
|
||||
if (value != null) return value; // for chaining in processOrder()
|
||||
visitOrderEntryRoots(myVisitor, libraryOrderEntry);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When a name is imported from a module, tries to find the definition of that name inside the module,
|
||||
* as opposed to looking for submodules.
|
||||
@@ -1051,34 +634,4 @@ public class ResolveImportUtil {
|
||||
}
|
||||
return PointInImport.NONE;
|
||||
}
|
||||
|
||||
public static class ResolveNameVisitor implements RootVisitor {
|
||||
final @NotNull PsiManager myPsiManager;
|
||||
private boolean myFound = false;
|
||||
private final String myName;
|
||||
|
||||
|
||||
public ResolveNameVisitor(@NotNull PsiManager psiManager, String name) {
|
||||
myPsiManager = psiManager;
|
||||
myName = name;
|
||||
}
|
||||
|
||||
public boolean visitRoot(final VirtualFile root) {
|
||||
if (!root.isValid()) {
|
||||
return true;
|
||||
}
|
||||
PsiElement module = root.isDirectory() ? myPsiManager.findDirectory(root) : myPsiManager.findFile(root);
|
||||
module = resolveChild(module, myName, null, root, false, false);
|
||||
if (module != null) {
|
||||
myFound = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isFound() {
|
||||
return myFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.jetbrains.python.psi.resolve;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.Processor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class RootVisitorHost {
|
||||
public static void visitRoots(@NotNull final PsiElement elt, @NotNull final RootVisitor visitor) {
|
||||
// real search
|
||||
final Module module = ModuleUtil.findModuleForPsiElement(elt);
|
||||
if (module != null) {
|
||||
visitRoots(module, false, visitor);
|
||||
}
|
||||
else {
|
||||
final PsiFile containingFile = elt.getContainingFile();
|
||||
if (containingFile != null) {
|
||||
visitSdkRoots(containingFile, visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void visitRoots(@NotNull Module module, final boolean skipSdk, final RootVisitor visitor) {
|
||||
OrderEnumerator enumerator = OrderEnumerator.orderEntries(module).recursively();
|
||||
if (skipSdk) {
|
||||
enumerator = enumerator.withoutSdk();
|
||||
}
|
||||
enumerator.forEach(new Processor<OrderEntry>() {
|
||||
@Override
|
||||
public boolean process(OrderEntry orderEntry) {
|
||||
if (orderEntry instanceof ModuleSourceOrderEntry) {
|
||||
return visitModuleContentEntries(((ModuleSourceOrderEntry)orderEntry).getRootModel(), visitor);
|
||||
}
|
||||
return visitOrderEntryRoots(visitor, orderEntry);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void visitSdkRoots(PsiFile file, RootVisitor visitor) {
|
||||
// formality
|
||||
final VirtualFile elt_vfile = file.getOriginalFile().getVirtualFile();
|
||||
List<OrderEntry> orderEntries = null;
|
||||
if (elt_vfile != null) { // reality
|
||||
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(file.getProject()).getFileIndex();
|
||||
orderEntries = fileIndex.getOrderEntriesForFile(elt_vfile);
|
||||
if (orderEntries.size() > 0) {
|
||||
for (OrderEntry entry : orderEntries) {
|
||||
if (!visitOrderEntryRoots(visitor, entry)) break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
orderEntries = null;
|
||||
}
|
||||
}
|
||||
|
||||
// out-of-project file or non-file(e.g. console) - use roots of SDK assigned to project
|
||||
if (orderEntries == null) {
|
||||
final Sdk sdk = ProjectRootManager.getInstance(file.getProject()).getProjectSdk();
|
||||
if (sdk != null) {
|
||||
visitSdkRoots(sdk, visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean visitSdkRoots(@NotNull Sdk sdk, @NotNull RootVisitor visitor) {
|
||||
final VirtualFile[] roots = sdk.getRootProvider().getFiles(OrderRootType.CLASSES);
|
||||
for (VirtualFile root : roots) {
|
||||
if (!visitor.visitRoot(root)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean visitModuleContentEntries(ModuleRootModel rootModel, RootVisitor visitor) {
|
||||
// look in module sources
|
||||
Set<VirtualFile> contentRoots = Sets.newHashSet();
|
||||
for (ContentEntry entry : rootModel.getContentEntries()) {
|
||||
VirtualFile rootFile = entry.getFile();
|
||||
|
||||
if (rootFile != null && !visitor.visitRoot(rootFile)) return false;
|
||||
contentRoots.add(rootFile);
|
||||
for (VirtualFile folder : entry.getSourceFolderFiles()) {
|
||||
if (!visitor.visitRoot(folder)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean visitOrderEntryRoots(RootVisitor visitor, OrderEntry entry) {
|
||||
Set<VirtualFile> allRoots = new LinkedHashSet<VirtualFile>();
|
||||
Collections.addAll(allRoots, entry.getFiles(OrderRootType.SOURCES));
|
||||
Collections.addAll(allRoots, entry.getFiles(OrderRootType.CLASSES));
|
||||
for (VirtualFile root : allRoots) {
|
||||
if (!visitor.visitRoot(root)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.codeInsight.PyDynamicMember;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -26,9 +27,13 @@ public class PyClassMembersProviderBase implements PyClassMembersProvider {
|
||||
public static PsiElement resolveMemberByName(Collection<PyDynamicMember> members,
|
||||
PyClassType clazz,
|
||||
String name) {
|
||||
final PyClass pyClass = clazz.getPyClass();
|
||||
if (pyClass == null) {
|
||||
return null;
|
||||
}
|
||||
for (PyDynamicMember member : members) {
|
||||
if (member.getName().equals(name)) {
|
||||
return member.resolve(clazz.getPyClass());
|
||||
return member.resolve(pyClass);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
+4
-4
@@ -122,8 +122,8 @@ public class PyIntroduceFieldHandler extends IntroduceHandler {
|
||||
final PyFunction function = (PyFunction)myScope;
|
||||
final PyParameter[] parameters = function.getParameterList().getParameters();
|
||||
if (parameters.length > 0 && result == parameters[0]) {
|
||||
final Set<PyFunction.Flag> flags = PyUtil.detectDecorationsAndWrappersOf(function);
|
||||
if (!(flags.contains(PyFunction.Flag.STATICMETHOD))) {
|
||||
final PyFunction.Modifier modifier = function.getModifier();
|
||||
if (modifier != PyFunction.Modifier.STATICMETHOD) {
|
||||
// 'self' is not a local scope dependency
|
||||
return;
|
||||
}
|
||||
@@ -247,8 +247,8 @@ public class PyIntroduceFieldHandler extends IntroduceHandler {
|
||||
private static boolean isInStaticMethod(PsiElement element) {
|
||||
PyFunction containingMethod = PsiTreeUtil.getParentOfType(element, PyFunction.class, false, PyClass.class);
|
||||
if (containingMethod != null) {
|
||||
final Set<PyFunction.Flag> flags = PyUtil.detectDecorationsAndWrappersOf(containingMethod);
|
||||
return flags.contains(PyFunction.Flag.STATICMETHOD);
|
||||
final PyFunction.Modifier modifier = containingMethod.getModifier();
|
||||
return modifier == PyFunction.Modifier.STATICMETHOD;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.highlighting.PyHighlighter;
|
||||
import com.jetbrains.python.psi.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -39,8 +37,8 @@ public class HighlightingAnnotator extends PyAnnotator {
|
||||
boolean isSelf = false;
|
||||
final int index = ArrayUtil.find(function.getParameterList().getParameters(), node);
|
||||
if (function.getContainingClass() != null && index == 0) {
|
||||
final Set<PyFunction.Flag> flags = PyUtil.detectDecorationsAndWrappersOf(function);
|
||||
if (!flags.contains(PyFunction.Flag.CLASSMETHOD) && !flags.contains(PyFunction.Flag.STATICMETHOD)) {
|
||||
final PyFunction.Modifier modifier = function.getModifier();
|
||||
if (modifier != PyFunction.Modifier.CLASSMETHOD && modifier != PyFunction.Modifier.STATICMETHOD) {
|
||||
isSelf = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ import <warning descr="Import resolves to its containing file">AddFunctionToModu
|
||||
|
||||
# do something
|
||||
|
||||
AddFunctionToModule.<caret><warning descr="Unresolved reference 'frob'">frob</warning>(1)
|
||||
AddFunctionToModule.<caret><warning descr="Cannot find reference 'frob' in 'AddFunctionToModule.py'">frob</warning>(1)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import collections.<warning descr="No module named OrderedDict">OrderedDict</warning>
|
||||
@@ -1 +1 @@
|
||||
import <warning descr="Unresolved reference 'wurm'">wurm</warning>
|
||||
import <warning descr="No module named wurm">wurm</warning>
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
import <warning descr="Unresolved reference 'deliverance'">deliverance</warning>.proxycommand
|
||||
import <warning descr="No module named deliverance">deliverance</warning>.proxycommand
|
||||
deliverance.proxycommand.main()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileFilter;
|
||||
import com.intellij.psi.*;
|
||||
@@ -210,14 +209,6 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
|
||||
assertResolvesTo(PyTargetExpression.class, "__all__");
|
||||
}
|
||||
|
||||
public void testDunderAllImport() {
|
||||
assertResolvesTo(PyTargetExpression.class, "__all__");
|
||||
}
|
||||
|
||||
public void testDunderAllImportResolve() {
|
||||
assertResolvesTo(PyTargetExpression.class, "__all__");
|
||||
}
|
||||
|
||||
public void testDunderAllConflict() {
|
||||
assertResolvesTo(PyFunction.class, "do_stuff", "/src/mypackage1.py");
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import com.jetbrains.python.psi.PyCallExpression;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Tests callee resolution in PyCallExpressionImpl.
|
||||
* User: dcheryasov
|
||||
@@ -31,7 +29,7 @@ public class PyResolveCalleeTest extends PyTestCase {
|
||||
public void testClassCall() {
|
||||
PyCallExpression.PyMarkedCallee resolved = resolveCallee();
|
||||
assertNotNull(resolved.getCallable());
|
||||
assertTrue(resolved.getFlags().equals(EnumSet.noneOf(PyFunction.Flag.class)));
|
||||
assertEquals(null, resolved.getModifier());
|
||||
}
|
||||
|
||||
public void testDecoCall() {
|
||||
@@ -43,13 +41,13 @@ public class PyResolveCalleeTest extends PyTestCase {
|
||||
public void testDecoParamCall() {
|
||||
PyCallExpression.PyMarkedCallee resolved = resolveCallee();
|
||||
assertNotNull(resolved.getCallable());
|
||||
assertTrue(resolved.getFlags().equals(EnumSet.noneOf(PyFunction.Flag.class)));
|
||||
assertNull(resolved.getModifier());
|
||||
}
|
||||
|
||||
public void testWrappedStaticMethod() {
|
||||
PyCallExpression.PyMarkedCallee resolved = resolveCallee();
|
||||
assertNotNull(resolved.getCallable());
|
||||
assertEquals(0, resolved.getImplicitOffset());
|
||||
assertEquals(resolved.getFlags(), EnumSet.of(PyFunction.Flag.STATICMETHOD));
|
||||
assertEquals(resolved.getModifier(), PyFunction.Modifier.STATICMETHOD);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ import com.jetbrains.python.psi.stubs.PyVariableNameIndex;
|
||||
import com.jetbrains.python.toolbox.Maybe;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
@@ -335,8 +333,8 @@ public class PyStubsTest extends PyTestCase {
|
||||
final PyClass pyClass = file.getTopLevelClasses().get(0);
|
||||
final PyFunction[] methods = pyClass.getMethods();
|
||||
assertEquals(1, methods.length);
|
||||
final Set<PyFunction.Flag> flags = PyUtil.detectDecorationsAndWrappersOf(methods[0]);
|
||||
assertEquals(EnumSet.of(PyFunction.Flag.STATICMETHOD), flags);
|
||||
final PyFunction.Modifier modifier = methods[0].getModifier();
|
||||
assertEquals(PyFunction.Modifier.STATICMETHOD, modifier);
|
||||
assertNotParsed(file);
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -73,6 +73,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testImportFunction() { // PY-1896
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testImportToContainingFile() { // PY-4372
|
||||
myFixture.copyFileToProject("inspections/PyUnresolvedReferencesInspection/__init__.py", "PyUnresolvedReferencesInspection/__init__.py");
|
||||
myFixture.copyFileToProject("inspections/PyUnresolvedReferencesInspection/importToContainingFile.py", "PyUnresolvedReferencesInspection/importToContainingFile.py");
|
||||
|
||||
Reference in New Issue
Block a user