Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Trofimov
2011-12-14 22:07:05 +01:00
48 changed files with 889 additions and 955 deletions
+9 -10
View File
@@ -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();
@@ -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();
@@ -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, "...");
}
@@ -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())
@@ -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();
@@ -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();
+4 -120
View File
@@ -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>
* &#x40;foo
* &#x40;classmethod <b># &lt;-- that's it</b>
* &#x40;bar
* def moo(cls):
* &nbsp;&nbsp;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>
* &#x40;foo
* &#x40;classmethod <b># &lt;-- that's it</b>
* &#x40;bar
* def moo(cls):
* &nbsp;&nbsp;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) {
@@ -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()) {
@@ -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;
@@ -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,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);
}
@@ -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");