diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java
index e33c618da225..a4d3156c9232 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java
@@ -1400,16 +1400,7 @@ public final class HighlightUtil {
}
if (!StringUtil.startsWithChar(text, '\"')) return null;
- if (StringUtil.endsWithChar(text, '\"')) {
- if (text.length() == 1) {
- String message = JavaErrorBundle.message("illegal.line.end.in.string.literal");
- if (description != null) {
- description.set(message);
- }
- return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
- }
- }
- else {
+ if (!StringUtil.endsWithChar(text, '\"') || text.length() == 1) {
String message = JavaErrorBundle.message("illegal.line.end.in.string.literal");
if (description != null) {
description.set(message);
@@ -1514,6 +1505,32 @@ public final class HighlightUtil {
return null;
}
+ public static HighlightInfo.Builder checkFragmentError(PsiFragment fragment) {
+ String text = fragment.getText();
+ int length = text.length();
+ if (fragment.getTokenType() == JavaTokenType.STRING_TEMPLATE_END) {
+ if (!StringUtil.endsWithChar(text, '\"') || length == 1) {
+ return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
+ .range(fragment)
+ .descriptionAndTooltip(JavaErrorBundle.message("illegal.line.end.in.string.literal"));
+ }
+ }
+ if (text.endsWith("\\{")) {
+ text = text.substring(0, length - 2);
+ length -= 2;
+ }
+ StringBuilder chars = new StringBuilder(length);
+ int[] offsets = new int[length + 1];
+ boolean success = CodeInsightUtilCore.parseStringCharacters(text, chars, offsets, fragment.isTextBlock());
+ if (!success) {
+ String message = JavaErrorBundle.message("illegal.escape.character.in.string.literal");
+ return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
+ .range(fragment, calculateErrorRange(text, offsets[chars.length()]))
+ .descriptionAndTooltip(message);
+ }
+ return null;
+ }
+
private static HighlightInfo.@Nullable Builder checkStringTemplateEscapes(@NotNull PsiLiteralExpression expression,
@NotNull String text,
@NotNull LanguageLevel level,
diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java
index d2439f9102a8..35eaab04245e 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java
@@ -943,9 +943,15 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
super.visitTemplateExpression(expression);
add(HighlightUtil.checkTemplateExpression(expression));
- if (!myHolder.hasErrorResults()) {
- add(HighlightUtil.checkUnhandledExceptions(expression));
- }
+ if (!myHolder.hasErrorResults()) add(HighlightUtil.checkUnhandledExceptions(expression));
+ }
+
+ @Override
+ public void visitFragment(@NotNull PsiFragment fragment) {
+ super.visitFragment(fragment);
+
+ HighlightUtil.checkIllegalUnicodeEscapes(fragment, myHolder);
+ if (!myHolder.hasErrorResults()) add(HighlightUtil.checkFragmentError(fragment));
}
@Override
diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaHighlightErrorFilter.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaHighlightErrorFilter.java
index 7be809bcce86..cac28c38b2d8 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaHighlightErrorFilter.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaHighlightErrorFilter.java
@@ -25,24 +25,12 @@ public class JavaHighlightErrorFilter extends HighlightErrorFilter {
}
else {
// reporting missing semicolons after an unclosed string literal is not useful.
- PsiElement prevLeaf = PsiTreeUtil.prevCodeLeaf(element);
- if (prevLeaf instanceof PsiJavaToken token) {
- IElementType type = token.getTokenType();
- if (type == JavaTokenType.STRING_LITERAL) {
- String text = token.getText();
- if (text.length() == 1 || !StringUtil.endsWithChar(text, '"')) {
- return false;
- }
- }
- else if (type == JavaTokenType.CHARACTER_LITERAL) {
- String text = token.getText();
- if (text.length() == 1 || !StringUtil.endsWithChar(text, '\'')) {
- return false;
- }
- }
- }
+ if (isAfterUnclosedStringLiteral(element)) return false;
}
}
+ else if (description.equals(JavaPsiBundle.message("expected.comma.or.rparen"))) {
+ if (isAfterUnclosedStringLiteral(element)) return false;
+ }
else if (description.equals(JavaPsiBundle.message("expected.class.or.interface"))) {
String text = element.getText();
if ((text.equals(PsiKeyword.SEALED) || text.equals(PsiKeyword.NON_SEALED)) &&
@@ -52,4 +40,33 @@ public class JavaHighlightErrorFilter extends HighlightErrorFilter {
}
return true;
}
+
+ private static boolean isAfterUnclosedStringLiteral(@NotNull PsiErrorElement element) {
+ PsiElement prevLeaf = PsiTreeUtil.prevCodeLeaf(element);
+ if (prevLeaf instanceof PsiJavaToken token) {
+ IElementType type = token.getTokenType();
+ if (type == JavaTokenType.STRING_LITERAL) {
+ String text = token.getText();
+ if (text.length() == 1 || !StringUtil.endsWithChar(text, '"')) {
+ return true;
+ }
+ }
+ else if (type == JavaTokenType.CHARACTER_LITERAL) {
+ String text = token.getText();
+ if (text.length() == 1 || !StringUtil.endsWithChar(text, '\'')) {
+ return true;
+ }
+ }
+ }
+ else if (prevLeaf instanceof PsiFragment fragment) {
+ IElementType type = fragment.getTokenType();
+ if (type == JavaTokenType.STRING_TEMPLATE_END) {
+ String text = fragment.getText();
+ if (text.length() == 1 || !StringUtil.endsWithChar(text, '"')) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java
index 23b1d3df70d3..6e35733779f0 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java
@@ -40,4 +40,13 @@ class X {
void nested() {
System.out.println(STR."\{STR."\{STR."\{STR."\{STR."\{STR."\{STR.""}"}"}"}"}"}");
}
+
+ String badEscape() {
+ System.out.println(STR."b\ad \{} esc\ape 1");
+ System.out.println(STR. """
+ b\ad \{} esc\ape 2
+ """);
+ System.out.println(STR."\{}unclosed);
+ return STR."\{} \uX";
+ }
}
\ No newline at end of file