diff --git a/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java b/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java index 8e1e393109a4..6235cbe1246d 100644 --- a/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java +++ b/python/psi-api/src/com/jetbrains/python/PyTokenTypes.java @@ -186,4 +186,12 @@ public class PyTokenTypes { public static final PyElementType FSTRING_FRAGMENT_END = new PyElementType("FSTRING_FRAGMENT_END"); public static final PyElementType FSTRING_FRAGMENT_FORMAT_START = new PyElementType("FSTRING_FRAGMENT_FORMAT_START"); public static final PyElementType FSTRING_FRAGMENT_TYPE_CONVERSION = new PyElementType("FSTRING_FRAGMENT_TYPE_CONVERSION"); + + public static final TokenSet FSTRING_TOKENS = TokenSet.create(FSTRING_TEXT, + FSTRING_START, + FSTRING_END, + FSTRING_FRAGMENT_START, + FSTRING_FRAGMENT_END, + FSTRING_FRAGMENT_FORMAT_START, + FSTRING_FRAGMENT_TYPE_CONVERSION); } diff --git a/python/src/com/jetbrains/python/PythonParserDefinition.java b/python/src/com/jetbrains/python/PythonParserDefinition.java index d829f7658784..34a460ec4138 100644 --- a/python/src/com/jetbrains/python/PythonParserDefinition.java +++ b/python/src/com/jetbrains/python/PythonParserDefinition.java @@ -53,7 +53,7 @@ public class PythonParserDefinition implements ParserDefinition { @Override @NotNull public TokenSet getStringLiteralElements() { - return TokenSet.orSet(PyTokenTypes.STRING_NODES, TokenSet.create(PyElementTypes.STRING_LITERAL_EXPRESSION)); + return TokenSet.orSet(PyTokenTypes.STRING_NODES, PyTokenTypes.FSTRING_TOKENS); } @Override diff --git a/python/testData/refactoring/rename/referencesInsideFStringsNotReportedAsStringOccurrences.py b/python/testData/refactoring/rename/referencesInsideFStringsNotReportedAsStringOccurrences.py new file mode 100644 index 000000000000..9dad5d1d0724 --- /dev/null +++ b/python/testData/refactoring/rename/referencesInsideFStringsNotReportedAsStringOccurrences.py @@ -0,0 +1,3 @@ +def func(): + var = 42 + s = f'{var}' \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java index 77175b277f88..ed476a3a8fa6 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyRenameTest.java @@ -5,15 +5,20 @@ import com.intellij.codeInsight.TargetElementUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.util.TextOccurrencesUtil; import com.intellij.testFramework.PlatformTestUtil; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PythonTestUtil; import com.jetbrains.python.documentation.docstrings.DocStringFormat; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.psi.PyTargetExpression; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * @author yole @@ -323,6 +328,19 @@ public class PyRenameTest extends PyTestCase { assertEquals("foo_bar_baz", PyNameSuggestionProvider.toUnderscores("foo_BarBAZ")); } + // PY-27749 + public void testReferencesInsideFStringsNotReportedAsStringOccurrences() { + myFixture.configureByFile(RENAME_DATA_PATH + getTestName(true) + ".py"); + final PyTargetExpression attr = (PyTargetExpression)myFixture.getElementAtCaret(); + final GlobalSearchScope singleFileScope = GlobalSearchScope.fileScope(myFixture.getFile()); + final List found = new ArrayList<>(); + TextOccurrencesUtil.processUsagesInStringsAndComments(attr, singleFileScope, attr.getName(), true, (psiElement, textRange) -> { + found.add(psiElement); + return true; + }); + assertEmpty(found); + } + private void renameWithDocStringFormat(DocStringFormat format, final String newName) { runWithDocStringFormat(format, () -> doTest(newName)); }