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();