From 0bc7a9171652043f5ef3d8440faa80aeb35fc202 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Tue, 1 Nov 2016 17:19:38 +0300 Subject: [PATCH] PY-21244 Suppress intention if references for some nested fields are unresolved --- .../PyConvertToFStringIntention.java | 36 +++++++++++++------ .../formatMethodParentFieldUnresolved.py | 1 + .../PyConvertToFStringIntentionTest.java | 5 +++ 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 python/testData/intentions/PyConvertToFStringIntentionTest/formatMethodParentFieldUnresolved.py diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertToFStringIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertToFStringIntention.java index 57a663c513e9..22cf27e448be 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertToFStringIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertToFStringIntention.java @@ -25,6 +25,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.ObjectUtils; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; @@ -85,22 +86,21 @@ public class PyConvertToFStringIntention extends PyBaseIntentionAction { return false; } - final List chunks = percentOperator ? PyStringFormatParser.parsePercentFormat(stringText) - : PyStringFormatParser.parseNewStyleFormat(stringText); - final List substitutions = PyStringFormatParser.filterSubstitutions(chunks); - - // TODO handle dynamic format spec in both formatting styles - final boolean hasDynamicFormatting; + final List substitutions; if (percentOperator) { - hasDynamicFormatting = substitutions.stream().anyMatch(s -> "*".equals(s.getWidth()) || "*".equals(s.getPrecision())); + final List chunks = PyStringFormatParser.parsePercentFormat(stringText); + substitutions = PyStringFormatParser.filterSubstitutions(chunks); } else { - hasDynamicFormatting = false; + substitutions = new ArrayList<>(PyNewStyleStringFormatParser.parse(stringText).getFields()); } - if (hasDynamicFormatting) return false; - final PySubstitutionChunkReference[] references = - PythonFormattedStringReferenceProvider.getReferencesFromChunks(pyString, substitutions, percentOperator); + // TODO handle dynamic width and precision in old-style/"percent" formatting + if (percentOperator && substitutions.stream().anyMatch(s -> "*".equals(s.getWidth()) || "*".equals(s.getPrecision()))) { + return false; + } + + final PySubstitutionChunkReference[] references = createChunkReferences(substitutions, pyString, percentOperator); final PsiElement valuesSource; if (percentOperator) { @@ -134,6 +134,20 @@ public class PyConvertToFStringIntention extends PyBaseIntentionAction { return false; } + @NotNull + private static PySubstitutionChunkReference[] createChunkReferences(@NotNull List substitutions, + @NotNull PyStringLiteralExpression pyString, + boolean percentOperator) { + if (percentOperator) { + return PythonFormattedStringReferenceProvider.getReferencesFromChunks(pyString, substitutions, true); + } + return substitutions.stream() + .filter(PyNewStyleStringFormatParser.Field.class::isInstance) + .map(PyNewStyleStringFormatParser.Field.class::cast) + .map(field -> new PySubstitutionChunkReference(pyString, field, ObjectUtils.chooseNotNull(field.getAutoPosition(), 0), false)) + .toArray(PySubstitutionChunkReference[]::new); + } + private static boolean expressionCanBeInlined(@NotNull PyStringLiteralExpression host, @NotNull PyExpression target) { // Cannot inline multi-line expressions or expressions that contains backslashes (yet) if (target.textContains('\\') || target.textContains('\n')) return false; diff --git a/python/testData/intentions/PyConvertToFStringIntentionTest/formatMethodParentFieldUnresolved.py b/python/testData/intentions/PyConvertToFStringIntentionTest/formatMethodParentFieldUnresolved.py new file mode 100644 index 000000000000..e92f2f703191 --- /dev/null +++ b/python/testData/intentions/PyConvertToFStringIntentionTest/formatMethodParentFieldUnresolved.py @@ -0,0 +1 @@ +'{foo:{bar}}'.format(bar=42) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/intentions/PyConvertToFStringIntentionTest.java b/python/testSrc/com/jetbrains/python/intentions/PyConvertToFStringIntentionTest.java index 7d1f2528a489..e796cf0def50 100644 --- a/python/testSrc/com/jetbrains/python/intentions/PyConvertToFStringIntentionTest.java +++ b/python/testSrc/com/jetbrains/python/intentions/PyConvertToFStringIntentionTest.java @@ -163,4 +163,9 @@ public class PyConvertToFStringIntentionTest extends PyIntentionTestCase { public void testFormatMethodNestedFields3() { doTest(); } + + // PY-21244 + public void testFormatMethodParentFieldUnresolved() { + doNegativeTest(); + } }