diff --git a/java/java-tests/testData/inspection/dataFlow/contractCheck/ConditionsConflict.java b/java/java-tests/testData/inspection/dataFlow/contractCheck/ConditionsConflict.java
index d22a85b86af0..9dc6e7674f72 100644
--- a/java/java-tests/testData/inspection/dataFlow/contractCheck/ConditionsConflict.java
+++ b/java/java-tests/testData/inspection/dataFlow/contractCheck/ConditionsConflict.java
@@ -8,7 +8,7 @@ class Foo {
@Contract("!null -> !null; !null -> null")
public native String repeating(String x);
- @Contract("true, false, _ -> !null; true, true, _\u0020-> null; true, _, _ -> fail")
+ @Contract("true, false, _ ->\40!null; true, true,\040_\u0020-> null; true, _, _ -> fail")
public native String booleanProblem(boolean x, boolean y, String z);
@Contract("true, false, _ -> !null; true, true, _ -> null; false, _, _ -> fail")
diff --git a/platform/core-impl/src/com/intellij/codeInsight/CodeInsightUtilCore.java b/platform/core-impl/src/com/intellij/codeInsight/CodeInsightUtilCore.java
index 09f03b90739a..47e4e08bc26c 100644
--- a/platform/core-impl/src/com/intellij/codeInsight/CodeInsightUtilCore.java
+++ b/platform/core-impl/src/com/intellij/codeInsight/CodeInsightUtilCore.java
@@ -18,6 +18,7 @@ package com.intellij.codeInsight;
import com.intellij.lang.Language;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
+import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -212,9 +213,71 @@ public abstract class CodeInsightUtilCore extends FileModificationService {
}
}
if (sourceOffsets != null) {
- sourceOffsets[outChars.length()-outOffset] = index;
+ sourceOffsets[outChars.length() - outOffset] = index;
}
}
return true;
}
+
+ /**
+ * Maps the substring range inside Java String literal value back into the source code range.
+ *
+ * @param text string literal as present in source code (including quotes)
+ * @param from start offset inside the represented string
+ * @param to end offset inside the represented string
+ * @return the range which represents the corresponding substring inside source representation,
+ * or null if from/to values are out of bounds.
+ */
+ @Nullable
+ public static TextRange mapBackStringRange(@NotNull String text, int from, int to) {
+ if (from > to || to < 0) return null;
+ if (text.startsWith("`")) {
+ // raw string
+ return new TextRange(from + 1, to + 1);
+ }
+ if (!text.startsWith("\"")) {
+ return null;
+ }
+ if (text.indexOf('\\') == -1) {
+ return new TextRange(from + 1, to + 1);
+ }
+ int curOffset = 0;
+ int mappedFrom = -1, mappedTo = -1;
+ int end = text.length() - 1;
+ int i = 1;
+ while (i <= end) {
+ if (curOffset == from) {
+ mappedFrom = i;
+ }
+ if (curOffset == to) {
+ mappedTo = i;
+ break;
+ }
+ if (i == end) break;
+ char c = text.charAt(i++);
+ if (c == '\\') {
+ if (i == end) return null;
+ // like \u0020
+ char c1 = text.charAt(i++);
+ if (c1 == 'u') {
+ while (i < end && text.charAt(i) == 'u') i++;
+ i += 4;
+ } else if (c1 >= '0' && c1 <= '7') { // octal escape
+ char c2 = i < end ? text.charAt(i) : 0;
+ if (c2 >= '0' && c2 <= '7') {
+ i++;
+ char c3 = i < end ? text.charAt(i) : 0;
+ if (c3 >= '0' && c3 <= '7' && c1 <= '3') {
+ i++;
+ }
+ }
+ }
+ }
+ curOffset++;
+ }
+ if (mappedFrom >= 0 && mappedTo >= 0) {
+ return new TextRange(mappedFrom, mappedTo);
+ }
+ return null;
+ }
}
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java
index 5852ab154f6e..82e2186679c6 100644
--- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java
+++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java
@@ -15,10 +15,7 @@
*/
package com.siyeh.ig.psiutils;
-import com.intellij.codeInsight.AnnotationUtil;
-import com.intellij.codeInsight.ExpressionUtil;
-import com.intellij.codeInsight.NullableNotNullManager;
-import com.intellij.codeInsight.PsiEquivalenceUtil;
+import com.intellij.codeInsight.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
@@ -1258,43 +1255,7 @@ public class ExpressionUtils {
if (expression instanceof PsiLiteralExpression) {
String value = tryCast(((PsiLiteralExpression)expression).getValue(), String.class);
if (value == null || value.length() < from || value.length() < to) return null;
- String text = expression.getText();
- if (text.startsWith("`")) {
- // raw-string
- return new TextRange(1 + from, 1 + to);
- }
- if (text.startsWith("\"")) {
- int curOffset = 0;
- int mappedFrom = -1, mappedTo = -1;
- int end = text.length() - 1;
- int i = 1;
- while (i <= end) {
- if (curOffset == from) {
- mappedFrom = i;
- }
- if (curOffset == to) {
- mappedTo = i;
- break;
- }
- if (i == end) break;
- char c = text.charAt(i);
- if (c == '\\') {
- i++;
- if (i == end) return null;
- // like \u0020
- if (text.charAt(i) == 'u') {
- while (i < end && text.charAt(i) == 'u') i++;
- i += 3;
- }
- }
- curOffset++;
- i++;
- }
- if (mappedFrom >= 0 && mappedTo >= 0) {
- return new TextRange(mappedFrom, mappedTo);
- }
- }
- return null;
+ return CodeInsightUtilCore.mapBackStringRange(expression.getText(), from, to);
}
if (expression instanceof PsiParenthesizedExpression) {
PsiExpression operand = ((PsiParenthesizedExpression)expression).getExpression();