mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
PY-16906 Resolve references to global variables in module-level docstrings
Because DocStringParameterReference doesn't implement PsiPolyVariantReference I had to adjust helper methods like PyResolveTestCase#findReferenceByMarker and PyMultiFileResolveTestCase#doResolve in resolve tests to handle normal PsiReference reference under cursor.
This commit is contained in:
@@ -49,7 +49,7 @@ public class DocStringParameterReference extends PsiReferenceBase<PyStringLitera
|
||||
myType = refType;
|
||||
}
|
||||
|
||||
public enum ReferenceType {PARAMETER, PARAMETER_TYPE, KEYWORD, VARIABLE, CLASS_VARIABLE, INSTANCE_VARIABLE}
|
||||
public enum ReferenceType {PARAMETER, PARAMETER_TYPE, KEYWORD, VARIABLE, CLASS_VARIABLE, INSTANCE_VARIABLE, GLOBAL_VARIABLE}
|
||||
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
@@ -78,6 +78,19 @@ public class DocStringParameterReference extends PsiReferenceBase<PyStringLitera
|
||||
return resolveInstanceVariable((PyClass)owner);
|
||||
}
|
||||
}
|
||||
if (owner instanceof PyFile && myType == ReferenceType.GLOBAL_VARIABLE) {
|
||||
return resolveGlobalVariable(((PyFile)owner));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement resolveGlobalVariable(@NotNull PyFile owner) {
|
||||
for (PyTargetExpression assignment : owner.getTopLevelAttributes()) {
|
||||
if (getCanonicalText().equals(assignment.getName())) {
|
||||
return assignment;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,10 @@ import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.PsiReferenceProvider;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringParameterReference.ReferenceType;
|
||||
import com.jetbrains.python.psi.PyImportElement;
|
||||
import com.jetbrains.python.psi.PyStringLiteralExpression;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.psi.StructuredDocString;
|
||||
import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
@@ -59,29 +61,30 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
|
||||
final TagBasedDocString taggedDocString = (TagBasedDocString)docString;
|
||||
result.addAll(referencesFromNames(expr, offset, docString,
|
||||
taggedDocString.getTagArguments(TagBasedDocString.PARAM_TAGS),
|
||||
DocStringParameterReference.ReferenceType.PARAMETER));
|
||||
ReferenceType.PARAMETER));
|
||||
result.addAll(referencesFromNames(expr, offset, docString,
|
||||
taggedDocString.getTagArguments(TagBasedDocString.PARAM_TYPE_TAGS),
|
||||
DocStringParameterReference.ReferenceType.PARAMETER_TYPE));
|
||||
ReferenceType.PARAMETER_TYPE));
|
||||
result.addAll(referencesFromNames(expr, offset, docString,
|
||||
docString.getKeywordArgumentSubstrings(), DocStringParameterReference.ReferenceType.KEYWORD));
|
||||
docString.getKeywordArgumentSubstrings(), ReferenceType.KEYWORD));
|
||||
|
||||
result.addAll(referencesFromNames(expr, offset, docString,
|
||||
taggedDocString.getTagArguments("var"),
|
||||
DocStringParameterReference.ReferenceType.VARIABLE));
|
||||
ReferenceType.VARIABLE));
|
||||
result.addAll(referencesFromNames(expr, offset, docString,
|
||||
taggedDocString.getTagArguments("cvar"),
|
||||
DocStringParameterReference.ReferenceType.CLASS_VARIABLE));
|
||||
ReferenceType.CLASS_VARIABLE));
|
||||
result.addAll(referencesFromNames(expr, offset, docString,
|
||||
taggedDocString.getTagArguments("ivar"),
|
||||
DocStringParameterReference.ReferenceType.INSTANCE_VARIABLE));
|
||||
ReferenceType.INSTANCE_VARIABLE));
|
||||
result.addAll(returnTypes(element, docString, offset));
|
||||
}
|
||||
else if (docString instanceof SectionBasedDocString) {
|
||||
final SectionBasedDocString sectioned = (SectionBasedDocString)docString;
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getParameterFields(), DocStringParameterReference.ReferenceType.PARAMETER));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getKeywordArgumentFields(), DocStringParameterReference.ReferenceType.KEYWORD));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getAttributeFields(), DocStringParameterReference.ReferenceType.INSTANCE_VARIABLE));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getParameterFields(), ReferenceType.PARAMETER));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getKeywordArgumentFields(), ReferenceType.KEYWORD));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getAttributeFields(),
|
||||
PyUtil.isTopLevel(element) ? ReferenceType.GLOBAL_VARIABLE : ReferenceType.INSTANCE_VARIABLE));
|
||||
result.addAll(referencesFromFields(expr, offset, sectioned.getReturnFields(), null));
|
||||
}
|
||||
return result.toArray(new PsiReference[result.size()]);
|
||||
@@ -105,7 +108,7 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
|
||||
int offset,
|
||||
@NotNull StructuredDocString docString,
|
||||
@NotNull List<Substring> paramNames,
|
||||
@NotNull DocStringParameterReference.ReferenceType refType) {
|
||||
@NotNull ReferenceType refType) {
|
||||
List<PsiReference> result = new ArrayList<PsiReference>();
|
||||
for (Substring name : paramNames) {
|
||||
final String s = name.toString();
|
||||
@@ -113,7 +116,7 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
|
||||
final TextRange range = name.getTextRange().shiftRight(offset);
|
||||
result.add(new DocStringParameterReference(element, range, refType));
|
||||
}
|
||||
if (refType.equals(DocStringParameterReference.ReferenceType.PARAMETER_TYPE)) {
|
||||
if (refType.equals(ReferenceType.PARAMETER_TYPE)) {
|
||||
final Substring type = docString.getParamTypeSubstring(s);
|
||||
if (type != null) {
|
||||
result.addAll(parseTypeReferences(element, type, offset));
|
||||
@@ -127,7 +130,7 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
|
||||
private static List<PsiReference> referencesFromFields(@NotNull PyStringLiteralExpression element,
|
||||
int offset,
|
||||
@NotNull List<SectionBasedDocString.SectionField> fields,
|
||||
@Nullable DocStringParameterReference.ReferenceType nameRefType) {
|
||||
@Nullable ReferenceType nameRefType) {
|
||||
final List<PsiReference> result = new ArrayList<PsiReference>();
|
||||
for (SectionBasedDocString.SectionField field : fields) {
|
||||
for (Substring nameSub: field.getNamesAsSubstrings()) {
|
||||
|
||||
10
python/testData/resolve/GoogleDocstringModuleAttribute.py
Normal file
10
python/testData/resolve/GoogleDocstringModuleAttribute.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""Attributes:
|
||||
|
||||
module_level_variable1 (int): Module level variables may be documented in
|
||||
<ref>
|
||||
either the ``Attributes`` section of the module docstring, or in an
|
||||
inline docstring immediately following the variable.
|
||||
|
||||
"""
|
||||
|
||||
module_level_variable1 = 12345
|
||||
@@ -16,7 +16,7 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.psi.PsiPolyVariantReference;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
|
||||
import com.jetbrains.python.codeInsight.imports.AddImportHelper.ImportPriority;
|
||||
import com.jetbrains.python.fixtures.PyResolveTestCase;
|
||||
@@ -164,7 +164,7 @@ public class PyAddImportTest extends PyTestCase {
|
||||
WriteCommandAction.runWriteCommandAction(myFixture.getProject(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final PsiPolyVariantReference reference = PyResolveTestCase.findReferenceByMarker(myFixture.getFile());
|
||||
final PsiReference reference = PyResolveTestCase.findReferenceByMarker(myFixture.getFile());
|
||||
if (qualifier != null) {
|
||||
AddImportHelper.addLocalFromImportStatement(reference.getElement(), qualifier, name);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import com.jetbrains.python.fixtures.PyMultiFileResolveTestCase;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -41,23 +43,23 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase {
|
||||
}
|
||||
|
||||
public void testFromImport() {
|
||||
ResolveResult[] results = doMultiResolve();
|
||||
assertTrue(results.length == 2); // func and import stmt
|
||||
PsiElement func_elt = results[0].getElement();
|
||||
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
|
||||
assertEquals("named 'func'?", "func", ((PyFunction)func_elt).getName());
|
||||
PsiElement import_elt = results[1].getElement();
|
||||
assertTrue("is import?", import_elt instanceof PyImportElement);
|
||||
List<PsiElement> results = doMultiResolve();
|
||||
assertSize(2, results); // func and import stmt
|
||||
PsiElement funcElt = results.get(0);
|
||||
assertTrue("is PyFunction?", funcElt instanceof PyFunction);
|
||||
assertEquals("named 'func'?", "func", ((PyFunction)funcElt).getName());
|
||||
PsiElement importElt = results.get(1);
|
||||
assertTrue("is import?", importElt instanceof PyImportElement);
|
||||
}
|
||||
|
||||
public void testFromImportStar() {
|
||||
ResolveResult[] results = doMultiResolve();
|
||||
assertTrue(results.length == 2); // func and import-* stmt
|
||||
PsiElement func_elt = results[0].getElement();
|
||||
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
|
||||
assertEquals("named 'func'?", "func", ((PyFunction)func_elt).getName());
|
||||
PsiElement import_elt = results[1].getElement();
|
||||
assertTrue("is import?", import_elt instanceof PyStarImportElement);
|
||||
List<PsiElement> results = doMultiResolve();
|
||||
assertSize(2, results); // func and import-* stmt
|
||||
PsiElement funcElt = results.get(0);
|
||||
assertTrue("is PyFunction?", funcElt instanceof PyFunction);
|
||||
assertEquals("named 'func'?", "func", ((PyFunction)funcElt).getName());
|
||||
PsiElement importElt = results.get(1);
|
||||
assertTrue("is import?", importElt instanceof PyStarImportElement);
|
||||
}
|
||||
|
||||
public void testFromPackageImport() {
|
||||
@@ -101,9 +103,9 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase {
|
||||
}
|
||||
|
||||
public void testTransitiveImport() {
|
||||
ResolveResult[] results = doMultiResolve();
|
||||
assertTrue(results.length == 2); // func and import stmt
|
||||
PsiElement elt = results[0].getElement();
|
||||
List<PsiElement> results = doMultiResolve();
|
||||
assertSize(2, results); // func and import stmt
|
||||
PsiElement elt = results.get(0);
|
||||
assertTrue("is target?", elt instanceof PyTargetExpression);
|
||||
}
|
||||
|
||||
@@ -116,13 +118,13 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase {
|
||||
}
|
||||
|
||||
public void testResolveInPkg() {
|
||||
ResolveResult[] results = doMultiResolve();
|
||||
assertTrue(results.length == 2); // func and import stmt
|
||||
PsiElement func_elt = results[0].getElement();
|
||||
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
|
||||
assertEquals("named 'token'?", "token", ((PyFunction)func_elt).getName());
|
||||
PsiElement import_elt = results[1].getElement();
|
||||
assertTrue("is import?", import_elt instanceof PyImportElement);
|
||||
List<PsiElement> results = doMultiResolve();
|
||||
assertSize(2, results); // func and import stmt
|
||||
final PsiElement funcElt = results.get(0);
|
||||
assertTrue("is PyFunction?", funcElt instanceof PyFunction);
|
||||
assertEquals("named 'token'?", "token", ((PyFunction)funcElt).getName());
|
||||
PsiElement importElt = results.get(1);
|
||||
assertTrue("is import?", importElt instanceof PyImportElement);
|
||||
}
|
||||
|
||||
public void testCircularImport() {
|
||||
|
||||
@@ -528,6 +528,16 @@ public class PyResolveTest extends PyResolveTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
// PY-16906
|
||||
public void testGoogleDocstringModuleAttribute() {
|
||||
runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
assertResolvesTo(PyTargetExpression.class, "module_level_variable1");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// PY-7541
|
||||
public void testLoopToUpperReassignment() {
|
||||
final PsiReference ref = findReferenceByMarker();
|
||||
|
||||
@@ -20,9 +20,15 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileFilter;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiManagerImpl;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import com.jetbrains.python.PythonTestUtil;
|
||||
import junit.framework.Assert;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -36,7 +42,7 @@ public abstract class PyMultiFileResolveTestCase extends PyResolveTestCase {
|
||||
}
|
||||
|
||||
protected PsiElement doResolve(PsiFile psiFile) {
|
||||
final PsiPolyVariantReference ref = PyResolveTestCase.findReferenceByMarker(psiFile);
|
||||
final PsiReference ref = PyResolveTestCase.findReferenceByMarker(psiFile);
|
||||
final PsiManagerImpl psiManager = (PsiManagerImpl)myFixture.getPsiManager();
|
||||
psiManager.setAssertOnFileLoadingFilter(new VirtualFileFilter() {
|
||||
@Override
|
||||
@@ -45,12 +51,16 @@ public abstract class PyMultiFileResolveTestCase extends PyResolveTestCase {
|
||||
return fileType == PythonFileType.INSTANCE;
|
||||
}
|
||||
}, myTestRootDisposable);
|
||||
final ResolveResult[] resolveResults = ref.multiResolve(false);
|
||||
psiManager.setAssertOnFileLoadingFilter(VirtualFileFilter.NONE, myTestRootDisposable);
|
||||
if (resolveResults.length == 0) {
|
||||
return null;
|
||||
final PsiElement result;
|
||||
if (ref instanceof PsiPolyVariantReference) {
|
||||
final ResolveResult[] resolveResults = ((PsiPolyVariantReference)ref).multiResolve(false);
|
||||
result = resolveResults.length == 0 || !resolveResults[0].isValidResult() ? null : resolveResults[0].getElement();
|
||||
}
|
||||
return resolveResults[0].isValidResult() ? resolveResults[0].getElement() : null;
|
||||
else {
|
||||
result = ref.resolve();
|
||||
}
|
||||
psiManager.setAssertOnFileLoadingFilter(VirtualFileFilter.NONE, myTestRootDisposable);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,9 +89,18 @@ public abstract class PyMultiFileResolveTestCase extends PyResolveTestCase {
|
||||
return doResolve(prepareFile());
|
||||
}
|
||||
|
||||
protected ResolveResult[] doMultiResolve() {
|
||||
PsiFile psiFile = prepareFile();
|
||||
final PsiPolyVariantReference ref = PyResolveTestCase.findReferenceByMarker(psiFile);
|
||||
return ref.multiResolve(false);
|
||||
@NotNull
|
||||
protected List<PsiElement> doMultiResolve() {
|
||||
final PsiFile psiFile = prepareFile();
|
||||
final PsiReference ref = PyResolveTestCase.findReferenceByMarker(psiFile);
|
||||
if (ref instanceof PsiPolyVariantReference) {
|
||||
return ContainerUtil.map(((PsiPolyVariantReference)ref).multiResolve(false), new Function<ResolveResult, PsiElement>() {
|
||||
@Override
|
||||
public PsiElement fun(ResolveResult result) {
|
||||
return result.getElement();
|
||||
}
|
||||
});
|
||||
}
|
||||
return Collections.singletonList(ref.resolve());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,9 +131,8 @@ public abstract class PyResolveTestCase extends PyTestCase {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PsiPolyVariantReference findReferenceByMarker(PsiFile psiFile) {
|
||||
int offset = findMarkerOffset(psiFile);
|
||||
final PsiPolyVariantReference ref = (PsiPolyVariantReference)psiFile.findReferenceAt(offset);
|
||||
public static PsiReference findReferenceByMarker(PsiFile psiFile) {
|
||||
final PsiReference ref = psiFile.findReferenceAt(findMarkerOffset(psiFile));
|
||||
assertNotNull("<ref> in test file not found", ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user