diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java index 0bf3f0d12e1f..8f970440f578 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java @@ -45,6 +45,8 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import static com.jetbrains.python.inspections.PyStringFormatParser.*; + /** * @author Alexey.Ivanov * @author vlan @@ -286,8 +288,9 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { final TextRange intersection = selectionRange.shiftRight(-offset).intersection(innerRange); final TextRange finalRange = intersection != null ? intersection : selectionRange; final String text = literal.getText(); - // TODO: Protect against substrings with new-style format characters - if (breaksStringFormatting(text, finalRange) || breaksStringEscaping(text, finalRange)) { + if (getFormatValueExpression(literal) != null && breaksStringFormatting(text, finalRange) || + getNewStyleFormatValueExpression(literal) != null && breaksNewStyleStringFormatting(text, finalRange) || + breaksStringEscaping(text, finalRange)) { showCannotPerformError(project, editor); return; } @@ -303,11 +306,15 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { } private boolean breaksStringFormatting(@NotNull String s, @NotNull TextRange range) { - return breaksRanges(PyStringFormatParser.substitutionsToRanges(new PyStringFormatParser(s).parseSubstitutions()), range); + return breaksRanges(substitutionsToRanges(new PyStringFormatParser(s).parseSubstitutions()), range); + } + + private boolean breaksNewStyleStringFormatting(@NotNull String s, @NotNull TextRange range) { + return breaksRanges(substitutionsToRanges(filterSubstitutions(parseNewStyleFormat(s))), range); } private boolean breaksStringEscaping(@NotNull String s, @NotNull TextRange range) { - return breaksRanges(PyStringFormatParser.getEscapeRanges(s), range); + return breaksRanges(getEscapeRanges(s), range); } private boolean breaksRanges(@NotNull List ranges, @NotNull TextRange range) { diff --git a/python/testData/refactoring/introduceVariable/substringBreaksNewStyleFormatChars.py b/python/testData/refactoring/introduceVariable/substringBreaksNewStyleFormatChars.py new file mode 100644 index 000000000000..53c21666f90a --- /dev/null +++ b/python/testData/refactoring/introduceVariable/substringBreaksNewStyleFormatChars.py @@ -0,0 +1 @@ +print("Hello {}!".format("World")) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java index 7b6f14c60821..e8f7f7052033 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceVariableTest.java @@ -188,6 +188,11 @@ public class PyIntroduceVariableTest extends PyIntroduceTestCase { doTest(); } + // PY-8372 + public void testSubstringBreaksNewStyleFormatChars() { + doTestCannotPerform(); + } + private void doTestCannotPerform() { boolean thrownExpectedException = false; try {