mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 04:51:24 +07:00
Java: error highlighting for string templates (IDEA-321503)
GitOrigin-RevId: d17a540698ecf5330326a7757475756ea2f85f86
This commit is contained in:
committed by
intellij-monorepo-bot
parent
4e43fa56a0
commit
6abbbdd360
@@ -1329,7 +1329,8 @@ public final class HighlightUtil {
|
||||
}
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
|
||||
}
|
||||
|
||||
final HighlightInfo.Builder info1 = checkStringTemplateEscapes(expression, text, level, file, description);
|
||||
if (info1 != null) return info1;
|
||||
int rawLength = rawText.length();
|
||||
StringBuilder chars = new StringBuilder(rawLength);
|
||||
int[] offsets = new int[rawLength + 1];
|
||||
@@ -1341,8 +1342,7 @@ public final class HighlightUtil {
|
||||
}
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
|
||||
.range(expression, calculateErrorRange(rawText, offsets[chars.length()]))
|
||||
.descriptionAndTooltip(message)
|
||||
;
|
||||
.descriptionAndTooltip(message);
|
||||
}
|
||||
int length = chars.length();
|
||||
if (length > 3) {
|
||||
@@ -1363,15 +1363,8 @@ public final class HighlightUtil {
|
||||
}
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
|
||||
}
|
||||
if (file != null && containsUnescaped(text, "\\s")) {
|
||||
HighlightInfo.Builder info = checkFeature(expression, HighlightingFeature.TEXT_BLOCK_ESCAPES, level, file);
|
||||
if (info != null) {
|
||||
if (description != null) {
|
||||
description.set(getUnsupportedFeatureMessage(HighlightingFeature.TEXT_BLOCK_ESCAPES, level, file));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
final HighlightInfo.Builder info = checkTextBlockEscapes(expression, text, level, file, description);
|
||||
if (info != null) return info;
|
||||
}
|
||||
else if (type == JavaTokenType.STRING_LITERAL || type == JavaTokenType.TEXT_BLOCK_LITERAL) {
|
||||
if (type == JavaTokenType.STRING_LITERAL) {
|
||||
@@ -1398,6 +1391,8 @@ public final class HighlightUtil {
|
||||
}
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
|
||||
}
|
||||
final HighlightInfo.Builder info1 = checkStringTemplateEscapes(expression, text, level, file, description);
|
||||
if (info1 != null) return info1;
|
||||
int length = rawText.length();
|
||||
StringBuilder chars = new StringBuilder(length);
|
||||
int[] offsets = new int[length + 1];
|
||||
@@ -1411,15 +1406,8 @@ public final class HighlightUtil {
|
||||
.range(expression, calculateErrorRange(rawText, offsets[chars.length()]))
|
||||
.descriptionAndTooltip(message);
|
||||
}
|
||||
if (file != null && containsUnescaped(text, "\\s")) {
|
||||
HighlightInfo.Builder info = checkFeature(expression, HighlightingFeature.TEXT_BLOCK_ESCAPES, level, file);
|
||||
if (info != null) {
|
||||
if (description != null) {
|
||||
description.set(getUnsupportedFeatureMessage(HighlightingFeature.TEXT_BLOCK_ESCAPES, level, file));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
final HighlightInfo.Builder info2 = checkTextBlockEscapes(expression, text, level, file, description);
|
||||
if (info2 != null) return info2;
|
||||
}
|
||||
else {
|
||||
if (!text.endsWith("\"\"\"")) {
|
||||
@@ -1444,6 +1432,8 @@ public final class HighlightUtil {
|
||||
}
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
|
||||
}
|
||||
final HighlightInfo.Builder info = checkStringTemplateEscapes(expression, text, level, file, description);
|
||||
if (info != null) return info;
|
||||
final int rawLength = rawText.length();
|
||||
StringBuilder chars = new StringBuilder(rawLength);
|
||||
int[] offsets = new int[rawLength + 1];
|
||||
@@ -1499,6 +1489,34 @@ public final class HighlightUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HighlightInfo.@Nullable Builder checkStringTemplateEscapes(@NotNull PsiLiteralExpression expression,
|
||||
@NotNull String text,
|
||||
@NotNull LanguageLevel level,
|
||||
@Nullable PsiFile file,
|
||||
@Nullable Ref<? super String> description) {
|
||||
if (file == null || !containsUnescaped(text, "\\{")) return null;
|
||||
HighlightInfo.Builder info = checkFeature(expression, HighlightingFeature.STRING_TEMPLATES, level, file);
|
||||
if (info == null) return null;
|
||||
if (description != null) {
|
||||
description.set(getUnsupportedFeatureMessage(HighlightingFeature.STRING_TEMPLATES, level, file));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
private static HighlightInfo.@Nullable Builder checkTextBlockEscapes(@NotNull PsiLiteralExpression expression,
|
||||
@NotNull String text,
|
||||
@NotNull LanguageLevel level,
|
||||
@Nullable PsiFile file,
|
||||
@Nullable Ref<? super String> description) {
|
||||
if (file == null || !containsUnescaped(text, "\\s")) return null;
|
||||
HighlightInfo.Builder info = checkFeature(expression, HighlightingFeature.TEXT_BLOCK_ESCAPES, level, file);
|
||||
if (info == null) return null;
|
||||
if (description != null) {
|
||||
description.set(getUnsupportedFeatureMessage(HighlightingFeature.TEXT_BLOCK_ESCAPES, level, file));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TextRange calculateErrorRange(@NotNull String rawText, int start) {
|
||||
int end;
|
||||
|
||||
@@ -909,6 +909,19 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (!myHolder.hasErrorResults()) add(HighlightUtil.checkLabelAlreadyInUse(statement));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTemplate(@NotNull PsiTemplate expression) {
|
||||
visitExpression(expression);
|
||||
|
||||
PsiElement parent = expression.getParent();
|
||||
if (!(parent instanceof PsiTemplateExpression)) {
|
||||
String message = JavaErrorBundle.message("processor.missing.from.string.template.expression");
|
||||
HighlightInfo.Builder highlightInfo =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(message);
|
||||
add(highlightInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLiteralExpression(@NotNull PsiLiteralExpression expression) {
|
||||
super.visitLiteralExpression(expression);
|
||||
|
||||
@@ -13,13 +13,13 @@ public class JavaHighlightingLexer extends LayeredLexer {
|
||||
public JavaHighlightingLexer(@NotNull LanguageLevel languageLevel) {
|
||||
super(JavaParserDefinition.createLexer(languageLevel));
|
||||
|
||||
registerSelfStoppingLayer(new JavaStringLiteralLexer('\"', JavaTokenType.STRING_LITERAL, false, "s"),
|
||||
registerSelfStoppingLayer(new JavaStringLiteralLexer('\"', JavaTokenType.STRING_LITERAL, false, "s{"),
|
||||
new IElementType[]{JavaTokenType.STRING_LITERAL}, IElementType.EMPTY_ARRAY);
|
||||
|
||||
registerSelfStoppingLayer(new JavaStringLiteralLexer('\'', JavaTokenType.STRING_LITERAL),
|
||||
registerSelfStoppingLayer(new JavaStringLiteralLexer('\'', JavaTokenType.STRING_LITERAL, false, "s"),
|
||||
new IElementType[]{JavaTokenType.CHARACTER_LITERAL}, IElementType.EMPTY_ARRAY);
|
||||
|
||||
registerSelfStoppingLayer(new JavaStringLiteralLexer(StringLiteralLexer.NO_QUOTE_CHAR, JavaTokenType.TEXT_BLOCK_LITERAL, true, "s"),
|
||||
registerSelfStoppingLayer(new JavaStringLiteralLexer(StringLiteralLexer.NO_QUOTE_CHAR, JavaTokenType.TEXT_BLOCK_LITERAL, true, "s{"),
|
||||
new IElementType[]{JavaTokenType.TEXT_BLOCK_LITERAL}, IElementType.EMPTY_ARRAY);
|
||||
|
||||
registerSelfStoppingLayer(new JavaStringLiteralLexer(StringLiteralLexer.NO_QUOTE_CHAR, JavaTokenType.TEXT_BLOCK_TEMPLATE_BEGIN, true, "s"),
|
||||
|
||||
@@ -358,6 +358,7 @@ floating.point.number.too.small=Floating point number too small
|
||||
illegal.underscore=Illegal underscore
|
||||
text.block.new.line=Illegal text block start: missing new line after opening quotes
|
||||
text.block.unclosed=Unclosed text block
|
||||
processor.missing.from.string.template.expression=Processor missing from string template expression
|
||||
|
||||
# suppress inspection "UnusedProperty"
|
||||
expected.identifier=Identifier expected
|
||||
|
||||
@@ -26,5 +26,7 @@ class UnsupportedFeatures {
|
||||
|
||||
String spaceEscapeSeq = <error descr="'\s' escape sequences are not supported at language level '1.4'">"\s"</error>;
|
||||
char c = <error descr="'\s' escape sequences are not supported at language level '1.4'">'\s'</error>;
|
||||
|
||||
String template = <error descr="Cannot resolve symbol 'STR'">STR</error>.<error descr="String templates are not supported at language level '1.4'">"Hello \{args[0]}"</error>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
class X {
|
||||
|
||||
void processorMissing() {
|
||||
System.out.println(<error descr="Processor missing from string template expression">"""
|
||||
\{1}
|
||||
"""</error>);
|
||||
<error descr="Processor missing from string template expression">"\{}"</error>;
|
||||
System.out.println(<error descr="Cannot resolve symbol 'NOPE'">NOPE</error>."\{false}");
|
||||
}
|
||||
|
||||
void correct(int i) {
|
||||
System.out.println(STR."the value is \{i}");
|
||||
}
|
||||
|
||||
String unresolvedValues() {
|
||||
return STR."\{<error descr="Cannot resolve symbol 'logic'">logic</error>} \{<error descr="Cannot resolve symbol 'proportion'">proportion</error>}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.codeInsight.daemon;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LightStringTemplatesHighlightingTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_21;
|
||||
}
|
||||
|
||||
public void testStringTemplates() { doTest(); }
|
||||
|
||||
private void doTest() {
|
||||
myFixture.addClass("""
|
||||
package java.lang;
|
||||
public interface StringTemplate {
|
||||
Processor<String, RuntimeException> STR = null;
|
||||
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES)
|
||||
@FunctionalInterface
|
||||
public interface Processor<R, E extends Throwable> {
|
||||
R process(StringTemplate stringTemplate) throws E;
|
||||
}
|
||||
}""");
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
myFixture.checkHighlighting();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user