diff --git a/config/projects-to-test/openjdk21-excluded.files b/config/projects-to-test/openjdk21-excluded.files
index c03fb35fae1..6385a12dcd1 100644
--- a/config/projects-to-test/openjdk21-excluded.files
+++ b/config/projects-to-test/openjdk21-excluded.files
@@ -29,14 +29,6 @@
-
-
-
-
-
-
-
-
diff --git a/pom.xml b/pom.xml
index 9e80811b499..ee8594f462a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1136,7 +1136,7 @@
BRANCH
COVEREDRATIO
- 0.71
+ 0.70
@@ -1154,7 +1154,7 @@
BRANCH
COVEREDRATIO
- 0.75
+ 0.73
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java b/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java
index 958134a6dd2..a4649125017 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java
@@ -107,6 +107,9 @@ public final class JavaAstVisitor extends JavaLanguageParserBaseVisitor startExpression = Optional.ofNullable(ctx.expr())
+ .map(this::visit);
+
+ if (startExpression.isPresent()) {
+ final DetailAstImpl imaginaryExpr =
+ createImaginary(TokenTypes.EMBEDDED_EXPRESSION);
+ imaginaryExpr.addChild(startExpression.orElseThrow());
+ begin.addChild(imaginaryExpr);
+ }
+
+ ctx.textBlockTemplateMiddle().stream()
+ .map(this::buildTextBlockTemplateMiddle)
+ .collect(Collectors.toUnmodifiableList())
+ .forEach(begin::addChild);
+
+ final DetailAstImpl end = buildTextBlockTemplateEnd(ctx);
+ begin.addChild(end);
+ return begin;
+ }
+
+ /**
+ * Builds the end of a text block template AST.
+ *
+ * @param ctx the TextBlockTemplateContext to build AST from
+ * @return text block template AST
+ */
+ private static DetailAstImpl buildTextBlockTemplateEnd(
+ JavaLanguageParser.TextBlockTemplateContext ctx) {
+
+ // token looks like '}' StringFragment '"""'
+ final TerminalNode context = ctx.TEXT_BLOCK_TEMPLATE_END();
+ final Token token = context.getSymbol();
+ final String tokenText = context.getText();
+ final int tokenStartIndex = token.getCharPositionInLine();
+ final int tokenLineNumber = token.getLine();
+ final int tokenTextLength = tokenText.length();
+
+ final DetailAstImpl embeddedExpressionEnd = createImaginary(
+ TokenTypes.EMBEDDED_EXPRESSION_END, EMBEDDED_EXPRESSION_END,
+ tokenLineNumber, tokenStartIndex
+ );
+
+ // remove delimiters '}' and '"'
+ final String stringFragment = tokenText.substring(
+ EMBEDDED_EXPRESSION_END.length(),
+ tokenTextLength - TRIPLE_QUOTE.length()
+ );
+
+ final DetailAstImpl endContent = createImaginary(
+ TokenTypes.TEXT_BLOCK_TEMPLATE_CONTENT, stringFragment,
+ tokenLineNumber,
+ tokenStartIndex + EMBEDDED_EXPRESSION_END.length()
+ );
+ embeddedExpressionEnd.addNextSibling(endContent);
+
+ final DetailAstImpl stringTemplateEnd = createImaginary(
+ TokenTypes.STRING_TEMPLATE_END, TRIPLE_QUOTE,
+ tokenLineNumber,
+ tokenStartIndex + tokenTextLength - TRIPLE_QUOTE.length()
+ );
+ endContent.addNextSibling(stringTemplateEnd);
+ return embeddedExpressionEnd;
+ }
+
+ /**
+ * Builds the middle of a text block template AST.
+ *
+ * @param middleContext the TextBlockTemplateMiddleContext to build AST from
+ * @return text block template middle AST
+ */
+ private DetailAstImpl buildTextBlockTemplateMiddle(
+ JavaLanguageParser.TextBlockTemplateMiddleContext middleContext) {
+
+ // token looks like '}' TextBlockFragment '\{'
+ final Token token = middleContext.middle;
+ final int tokenStartIndex = token.getCharPositionInLine();
+ final int tokenLineNumber = token.getLine();
+ final String tokenText = token.getText();
+ final int tokenTextLength = tokenText.length();
+
+ final DetailAstImpl embeddedExpressionEnd = createImaginary(
+ TokenTypes.EMBEDDED_EXPRESSION_END, EMBEDDED_EXPRESSION_END,
+ tokenLineNumber, tokenStartIndex
+ );
+
+ // remove delimiters '}' and '\\' '{'
+ final String stringFragment = tokenText.substring(
+ EMBEDDED_EXPRESSION_END.length(),
+ tokenTextLength - EMBEDDED_EXPRESSION_BEGIN.length()
+ );
+
+ final DetailAstImpl content = createImaginary(
+ TokenTypes.TEXT_BLOCK_TEMPLATE_CONTENT, stringFragment,
+ tokenLineNumber, tokenStartIndex + EMBEDDED_EXPRESSION_END.length()
+ );
+ embeddedExpressionEnd.addNextSibling(content);
+
+ final DetailAstImpl embeddedBegin = createImaginary(
+ TokenTypes.EMBEDDED_EXPRESSION_BEGIN,
+ EMBEDDED_EXPRESSION_BEGIN,
+ tokenLineNumber,
+ tokenStartIndex + tokenTextLength - EMBEDDED_EXPRESSION_BEGIN.length()
+ );
+ content.addNextSibling(embeddedBegin);
+
+ final Optional embeddedExpression =
+ Optional.ofNullable(middleContext.getChild(1))
+ .map(this::visit);
+
+ if (embeddedExpression.isPresent()) {
+ final DetailAstImpl imaginaryExpr =
+ createImaginary(TokenTypes.EMBEDDED_EXPRESSION);
+ imaginaryExpr.addChild(embeddedExpression.orElseThrow());
+ embeddedExpressionEnd.addNextSibling(imaginaryExpr);
+ }
+
+ return embeddedExpressionEnd;
+ }
+
+ /**
+ * Builds the beginning of a text block template AST.
+ *
+ * @param ctx the TextBlockTemplateContext to build AST from
+ * @return text block template AST
+ */
+ private static DetailAstImpl buildTextBlockTemplateBeginning(
+ JavaLanguageParser.TextBlockTemplateContext ctx) {
+
+ // token looks like '"' StringFragment '\{'
+ final TerminalNode context = ctx.TEXT_BLOCK_TEMPLATE_BEGIN();
+ final Token token = context.getSymbol();
+ final String tokenText = context.getText();
+ final int tokenStartIndex = token.getCharPositionInLine();
+ final int tokenLineNumber = token.getLine();
+ final int tokenTextLength = tokenText.length();
+
+ final DetailAstImpl textBlockTemplateBegin = createImaginary(
+ TokenTypes.TEXT_BLOCK_TEMPLATE_BEGIN, TRIPLE_QUOTE,
+ tokenLineNumber, tokenStartIndex
+ );
+
+ // remove delimiters '"' and '\{'
+ final String stringFragment = tokenText.substring(
+ TRIPLE_QUOTE.length(), tokenTextLength - EMBEDDED_EXPRESSION_BEGIN.length());
+
+ final DetailAstImpl stringTemplateContent = createImaginary(
+ TokenTypes.TEXT_BLOCK_TEMPLATE_CONTENT, stringFragment,
+ tokenLineNumber, tokenStartIndex + TRIPLE_QUOTE.length()
+ );
+ textBlockTemplateBegin.addChild(stringTemplateContent);
+
+ final DetailAstImpl embeddedBegin = createImaginary(
+ TokenTypes.EMBEDDED_EXPRESSION_BEGIN,
+ EMBEDDED_EXPRESSION_BEGIN,
+ tokenLineNumber,
+ tokenStartIndex + tokenTextLength - EMBEDDED_EXPRESSION_BEGIN.length()
+ );
+ textBlockTemplateBegin.addChild(embeddedBegin);
+ return textBlockTemplateBegin;
+ }
+
/**
* Builds the beginning of a string template AST.
*
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java b/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java
index 1a9dd37231d..48b85f4568d 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java
@@ -6786,6 +6786,153 @@ public final class TokenTypes {
public static final int UNNAMED_PATTERN_DEF =
JavaLanguageLexer.UNNAMED_PATTERN_DEF;
+ /**
+ * The opening delimiter of a text block template. This element ({@code """}) appears
+ * at the beginning of a text block template.
+ * For example:
+ * String s = STR."""
+ * Hello, \{ fetchName(uuid) }!
+ * """;
+ *
+ *
+ * parses as:
+ * `--VARIABLE_DEF -> VARIABLE_DEF
+ * |--MODIFIERS -> MODIFIERS
+ * |--TYPE -> TYPE
+ * | `--IDENT -> String
+ * |--IDENT -> s
+ * |--ASSIGN -> =
+ * | `--EXPR -> EXPR
+ * | `--DOT -> .
+ * | |--IDENT -> STR
+ * | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """
+ * | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n Hello,
+ * | |--EMBEDDED_EXPRESSION_BEGIN -> \{
+ * | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION
+ * | | `--METHOD_CALL -> (
+ * | | |--IDENT -> fetchName
+ * | | |--ELIST -> ELIST
+ * | | | `--EXPR -> EXPR
+ * | | | `--IDENT -> uuid
+ * | | `--RPAREN -> )
+ * | |--EMBEDDED_EXPRESSION_END -> }
+ * | |--TEXT_BLOCK_TEMPLATE_CONTENT -> !\n
+ * | `--STRING_TEMPLATE_END -> """
+ * `--SEMI -> ;
+ *
+ *
+ *
+ * @see #TEXT_BLOCK_TEMPLATE_END
+ * @see #TEXT_BLOCK_TEMPLATE_CONTENT
+ * @see #EMBEDDED_EXPRESSION_BEGIN
+ * @see #EMBEDDED_EXPRESSION
+ * @see #EMBEDDED_EXPRESSION_END
+ * @see #TEXT_BLOCK_LITERAL_BEGIN
+ *
+ * @since 10.13.0
+ */
+ public static final int TEXT_BLOCK_TEMPLATE_BEGIN =
+ JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_BEGIN;
+
+ /**
+ * The closing delimiter of a text block template. This element ({@code """}) appears
+ * at the end of a text block template.
+ * For example:
+ * String s = STR."""
+ * Sum: \{ calculateSum(x, y) }!
+ * """;
+ *
+ *
+ * parses as:
+ * `--VARIABLE_DEF -> VARIABLE_DEF
+ * |--MODIFIERS -> MODIFIERS
+ * |--TYPE -> TYPE
+ * | `--IDENT -> String
+ * |--IDENT -> s
+ * |--ASSIGN -> =
+ * | `--EXPR -> EXPR
+ * | `--DOT -> .
+ * | |--IDENT -> STR
+ * | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """
+ * | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n Sum:
+ * | |--EMBEDDED_EXPRESSION_BEGIN -> \{
+ * | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION
+ * | | `--METHOD_CALL -> (
+ * | | |--IDENT -> calculateSum
+ * | | |--ELIST -> ELIST
+ * | | | |--EXPR -> EXPR
+ * | | | | `--IDENT -> x
+ * | | | |--COMMA -> ,
+ * | | | `--EXPR -> EXPR
+ * | | | `--IDENT -> y
+ * | | `--RPAREN -> )
+ * | |--EMBEDDED_EXPRESSION_END -> }
+ * | |--TEXT_BLOCK_TEMPLATE_CONTENT -> !\n
+ * | `--STRING_TEMPLATE_END -> """
+ * `--SEMI -> ;
+ *
+ *
+ *
+ * @see #STRING_TEMPLATE_END
+ * @see #STRING_TEMPLATE_CONTENT
+ * @see #EMBEDDED_EXPRESSION_BEGIN
+ * @see #EMBEDDED_EXPRESSION
+ * @see #EMBEDDED_EXPRESSION_END
+ * @see #STRING_LITERAL
+ *
+ * @since 10.13.0
+ */
+ public static final int TEXT_BLOCK_TEMPLATE_END =
+ JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_END;
+
+ /**
+ * The content of a text block template. A given text block template may
+ * have more than one node of this type.
+ * For example:
+ * String s = STR."""
+ * dx/dy: \{ derivative("x^2") }!
+ * """;
+ *
+ *
+ * parses as:
+ * `--VARIABLE_DEF -> VARIABLE_DEF
+ * |--MODIFIERS -> MODIFIERS
+ * |--TYPE -> TYPE
+ * | `--IDENT -> String
+ * |--IDENT -> s
+ * |--ASSIGN -> =
+ * | `--EXPR -> EXPR
+ * | `--DOT -> .
+ * | |--IDENT -> STR
+ * | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """
+ * | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n dx/dy:
+ * | |--EMBEDDED_EXPRESSION_BEGIN -> \{
+ * | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION
+ * | | `--METHOD_CALL -> (
+ * | | |--IDENT -> derivative
+ * | | |--ELIST -> ELIST
+ * | | | `--EXPR -> EXPR
+ * | | | `--STRING_LITERAL -> "x^2"
+ * | | `--RPAREN -> )
+ * | |--EMBEDDED_EXPRESSION_END -> }
+ * | |--TEXT_BLOCK_TEMPLATE_CONTENT -> !\n
+ * | `--STRING_TEMPLATE_END -> """
+ * `--SEMI -> ;
+ *
+ *
+ *
+ * @see #STRING_TEMPLATE_END
+ * @see #STRING_TEMPLATE_CONTENT
+ * @see #EMBEDDED_EXPRESSION_BEGIN
+ * @see #EMBEDDED_EXPRESSION
+ * @see #EMBEDDED_EXPRESSION_END
+ * @see #STRING_LITERAL
+ *
+ * @since 10.13.0
+ */
+ public static final int TEXT_BLOCK_TEMPLATE_CONTENT =
+ JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_CONTENT;
+
/** Prevent instantiation. */
private TokenTypes() {
}
diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/grammar/java/JavaLanguageLexer.g4 b/src/main/resources/com/puppycrawl/tools/checkstyle/grammar/java/JavaLanguageLexer.g4
index daa92045502..22e2a1b5301 100644
--- a/src/main/resources/com/puppycrawl/tools/checkstyle/grammar/java/JavaLanguageLexer.g4
+++ b/src/main/resources/com/puppycrawl/tools/checkstyle/grammar/java/JavaLanguageLexer.g4
@@ -116,7 +116,8 @@ tokens {
STRING_TEMPLATE_CONTENT, EMBEDDED_EXPRESSION_BEGIN, EMBEDDED_EXPRESSION,
EMBEDDED_EXPRESSION_END,
- LITERAL_UNDERSCORE, UNNAMED_PATTERN_DEF
+ LITERAL_UNDERSCORE, UNNAMED_PATTERN_DEF, TEXT_BLOCK_TEMPLATE_BEGIN,
+ TEXT_BLOCK_TEMPLATE_MID, TEXT_BLOCK_TEMPLATE_END, TEXT_BLOCK_TEMPLATE_CONTENT
}
@header {
@@ -264,6 +265,10 @@ STRING_TEMPLATE_BEGIN: '"' StringFragment '\\' '{'
TEXT_BLOCK_LITERAL_BEGIN: '"' '"' '"' -> pushMode(TextBlock);
+TEXT_BLOCK_TEMPLATE_BEGIN: '"' '"' '"' TextBlockContent '\\' '{'
+ { contextCache.enterTemplateContext(TextBlockTemplate); }
+ ;
+
LITERAL_NULL: 'null';
// Separators
@@ -333,9 +338,6 @@ DOUBLE_COLON: '::';
AT: '@';
ELLIPSIS: '...';
-// String templates
-
-
// Text block fragments
fragment TextBlockContent
@@ -476,3 +478,12 @@ mode StringTemplate;
STRING_TEMPLATE_END: StringFragment '"'
{ contextCache.exitTemplateContext(); }
-> popMode, type(STRING_TEMPLATE_END);
+
+mode TextBlockTemplate;
+
+ TEXT_BLOCK_TEMPLATE_MID: TextBlockContent? '\\' '{'
+ -> pushMode(DEFAULT_MODE), type(TEXT_BLOCK_TEMPLATE_MID);
+
+ TEXT_BLOCK_TEMPLATE_END: TextBlockContent? '"' '"' '"'
+ { contextCache.exitTemplateContext(); }
+ -> popMode, type(TEXT_BLOCK_TEMPLATE_END);
diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/grammar/java/JavaLanguageParser.g4 b/src/main/resources/com/puppycrawl/tools/checkstyle/grammar/java/JavaLanguageParser.g4
index 8e121bb82a3..4ded1d302f3 100644
--- a/src/main/resources/com/puppycrawl/tools/checkstyle/grammar/java/JavaLanguageParser.g4
+++ b/src/main/resources/com/puppycrawl/tools/checkstyle/grammar/java/JavaLanguageParser.g4
@@ -760,21 +760,28 @@ primary
templateArgument
: template
+ | textBlockLiteral
| STRING_LITERAL
;
template
- : stringTemplate
- ;
+ : STRING_TEMPLATE_BEGIN
+ expr? stringTemplateMiddle*
+ STRING_TEMPLATE_END #stringTemplate
-stringTemplate
- : STRING_TEMPLATE_BEGIN expr? stringTemplateMiddle* STRING_TEMPLATE_END
+ | TEXT_BLOCK_TEMPLATE_BEGIN
+ expr? textBlockTemplateMiddle*
+ TEXT_BLOCK_TEMPLATE_END #textBlockTemplate
;
stringTemplateMiddle
: STRING_TEMPLATE_MID expr?
;
+textBlockTemplateMiddle
+ : middle=TEXT_BLOCK_TEMPLATE_MID expr?
+ ;
+
classType
: (classOrInterfaceType[false] DOT)? annotations[false] id typeArguments?
;
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/JavaAstVisitorTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/JavaAstVisitorTest.java
index 3caa27216f6..23252f8145b 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/JavaAstVisitorTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/JavaAstVisitorTest.java
@@ -90,10 +90,9 @@ public class JavaAstVisitorTest extends AbstractModuleTestSupport {
"visitQualifiedNameExtended",
"visitGuard",
- // until https://github.com/checkstyle/checkstyle/issues/14195
- "visitTemplate",
// handled as a list in the parent rule
- "visitStringTemplateMiddle"
+ "visitStringTemplateMiddle",
+ "visitTextBlockTemplateMiddle"
);
@Override
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/IllegalTokenTextCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/IllegalTokenTextCheckTest.java
index bb16cd7ec92..b4e15dc3843 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/IllegalTokenTextCheckTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/coding/IllegalTokenTextCheckTest.java
@@ -179,7 +179,7 @@ public void testOrderOfProperties() {
@Test
public void testAcceptableTokensMakeSense() {
- final int expectedTokenTypesTotalNumber = 195;
+ final int expectedTokenTypesTotalNumber = 198;
assertWithMessage("Total number of TokenTypes has changed, acceptable tokens in"
+ " IllegalTokenTextCheck need to be reconsidered.")
.that(TokenUtil.getTokenTypesTotalNumber())
@@ -197,7 +197,8 @@ public void testAcceptableTokensMakeSense() {
TokenTypes.STRING_LITERAL,
TokenTypes.CHAR_LITERAL,
TokenTypes.TEXT_BLOCK_CONTENT,
- TokenTypes.STRING_TEMPLATE_CONTENT
+ TokenTypes.STRING_TEMPLATE_CONTENT,
+ TokenTypes.TEXT_BLOCK_TEMPLATE_CONTENT
);
for (int tokenType : allowedTokens) {
assertWithMessage(TokenUtil.getTokenName(tokenType) + " should not be allowed"
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/grammar/GeneratedJavaTokenTypesTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/grammar/GeneratedJavaTokenTypesTest.java
index f0bc432cc45..3bb3e1433a7 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/grammar/GeneratedJavaTokenTypesTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/grammar/GeneratedJavaTokenTypesTest.java
@@ -763,6 +763,18 @@ public void testTokenNumbering() {
assertWithMessage(message)
.that(JavaLanguageLexer.UNNAMED_PATTERN_DEF)
.isEqualTo(225);
+ assertWithMessage(message)
+ .that(JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_BEGIN)
+ .isEqualTo(226);
+ assertWithMessage(message)
+ .that(JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_MID)
+ .isEqualTo(227);
+ assertWithMessage(message)
+ .that(JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_END)
+ .isEqualTo(228);
+ assertWithMessage(message)
+ .that(JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_CONTENT)
+ .isEqualTo(229);
final Set modeNames = Set.of(JavaLanguageLexer.modeNames);
final Set channelNames = Set.of(JavaLanguageLexer.channelNames);
@@ -780,7 +792,7 @@ public void testTokenNumbering() {
+ " 'GeneratedJavaTokenTypesTest' and verified"
+ " that their old numbering didn't change")
.that(tokenCount)
- .isEqualTo(225);
+ .isEqualTo(229);
}
/**
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/grammar/java21/Java21AstRegressionTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/grammar/java21/Java21AstRegressionTest.java
index 237a21e192a..f331d9bbe4e 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/grammar/java21/Java21AstRegressionTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/grammar/java21/Java21AstRegressionTest.java
@@ -128,8 +128,26 @@ public void testTextBlockParsingFail() throws Exception {
assertWithMessage(incorrectParsingFailureMessage)
.that(throwable.getCause().getMessage())
- .contains("13:14: mismatched input '}\\n"
- + " ' expecting TEXT_BLOCK_LITERAL_END");
+ .contains("12:15: no viable alternative at input"
+ + " '\"\"\"\\n \\{'");
}
+
+ @Test
+ public void testTextBlockTemplateBasic() throws Exception {
+ verifyAst(
+ getNonCompilablePath(
+ "ExpectedTextBlockTemplateBasic.txt"),
+ getNonCompilablePath(
+ "InputTextBlockTemplateBasic.java"));
+ }
+
+ @Test
+ public void testTextBlockTemplateInlineCodeTricky() throws Exception {
+ verifyAst(
+ getNonCompilablePath(
+ "ExpectedTextBlockTemplateInlineCodeTricky.txt"),
+ getNonCompilablePath(
+ "InputTextBlockTemplateInlineCodeTricky.java"));
+ }
}
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/utils/TokenUtilTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/utils/TokenUtilTest.java
index 577d8bc851d..4405de70d3d 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/utils/TokenUtilTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/utils/TokenUtilTest.java
@@ -228,7 +228,7 @@ public void testGetTokenTypesTotalNumber() {
assertWithMessage("Invalid token total number")
.that(tokenTypesTotalNumber)
- .isEqualTo(195);
+ .isEqualTo(198);
}
@Test
@@ -238,10 +238,10 @@ public void testGetAllTokenIds() {
assertWithMessage("Invalid token length")
.that(allTokenIds.length)
- .isEqualTo(195);
+ .isEqualTo(198);
assertWithMessage("invalid sum")
.that(sum)
- .isEqualTo(21142);
+ .isEqualTo(21825);
}
@Test
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateBasic.txt b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateBasic.txt
new file mode 100644
index 00000000000..d8433d3107b
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateBasic.txt
@@ -0,0 +1,181 @@
+COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
+|--PACKAGE_DEF -> package [2:0]
+| |--ANNOTATIONS -> ANNOTATIONS [2:47]
+| |--DOT -> . [2:47]
+| | |--DOT -> . [2:39]
+| | | |--DOT -> . [2:28]
+| | | | |--DOT -> . [2:22]
+| | | | | |--DOT -> . [2:11]
+| | | | | | |--IDENT -> com [2:8]
+| | | | | | `--IDENT -> puppycrawl [2:12]
+| | | | | `--IDENT -> tools [2:23]
+| | | | `--IDENT -> checkstyle [2:29]
+| | | `--IDENT -> grammar [2:40]
+| | `--IDENT -> java21 [2:48]
+| `--SEMI -> ; [2:54]
+`--CLASS_DEF -> CLASS_DEF [4:0]
+ |--MODIFIERS -> MODIFIERS [4:0]
+ | `--LITERAL_PUBLIC -> public [4:0]
+ |--LITERAL_CLASS -> class [4:7]
+ |--IDENT -> InputTextBlockTemplateBasic [4:13]
+ `--OBJBLOCK -> OBJBLOCK [4:41]
+ |--LCURLY -> { [4:41]
+ |--VARIABLE_DEF -> VARIABLE_DEF [6:4]
+ | |--MODIFIERS -> MODIFIERS [6:4]
+ | |--TYPE -> TYPE [6:4]
+ | | `--IDENT -> String [6:4]
+ | |--IDENT -> s [6:11]
+ | |--ASSIGN -> = [6:13]
+ | | `--EXPR -> EXPR [6:15]
+ | | `--STRING_LITERAL -> "my string" [6:15]
+ | `--SEMI -> ; [6:26]
+ |--VARIABLE_DEF -> VARIABLE_DEF [8:4]
+ | |--MODIFIERS -> MODIFIERS [8:4]
+ | |--TYPE -> TYPE [8:4]
+ | | `--IDENT -> String [8:4]
+ | |--IDENT -> s1 [8:11]
+ | |--ASSIGN -> = [8:14]
+ | | `--EXPR -> EXPR [8:19]
+ | | `--DOT -> . [8:19]
+ | | |--IDENT -> STR [8:16]
+ | | `--STRING_TEMPLATE_BEGIN -> " [8:20]
+ | | |--STRING_TEMPLATE_CONTENT -> ""\n "" [8:21]
+ | | `--STRING_TEMPLATE_END -> " [8:38]
+ | `--SEMI -> ; [9:15]
+ |--VARIABLE_DEF -> VARIABLE_DEF [10:4]
+ | |--MODIFIERS -> MODIFIERS [10:4]
+ | |--TYPE -> TYPE [10:4]
+ | | `--IDENT -> String [10:4]
+ | |--IDENT -> s2 [10:11]
+ | |--ASSIGN -> = [10:14]
+ | | `--EXPR -> EXPR [10:19]
+ | | `--DOT -> . [10:19]
+ | | |--IDENT -> STR [10:16]
+ | | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """ [10:20]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [10:23]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [10:36]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [12:12]
+ | | | `--IDENT -> s [12:12]
+ | | |--EMBEDDED_EXPRESSION_END -> } [13:12]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [13:13]
+ | | `--STRING_TEMPLATE_END -> """ [13:26]
+ | `--SEMI -> ; [14:15]
+ |--VARIABLE_DEF -> VARIABLE_DEF [15:4]
+ | |--MODIFIERS -> MODIFIERS [15:4]
+ | |--TYPE -> TYPE [15:4]
+ | | `--IDENT -> String [15:4]
+ | |--IDENT -> s3 [15:11]
+ | |--ASSIGN -> = [15:14]
+ | | `--EXPR -> EXPR [15:19]
+ | | `--DOT -> . [15:19]
+ | | |--IDENT -> STR [15:16]
+ | | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """ [15:20]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [15:23]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [15:36]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [17:12]
+ | | | `--IDENT -> s [17:12]
+ | | |--EMBEDDED_EXPRESSION_END -> } [18:12]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [20:12]
+ | | | `--IDENT -> s [20:12]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [18:13]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [18:26]
+ | | |--EMBEDDED_EXPRESSION_END -> } [21:12]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [21:13]
+ | | `--STRING_TEMPLATE_END -> """ [21:26]
+ | `--SEMI -> ; [22:15]
+ |--VARIABLE_DEF -> VARIABLE_DEF [24:4]
+ | |--MODIFIERS -> MODIFIERS [24:4]
+ | |--TYPE -> TYPE [24:4]
+ | | `--IDENT -> String [24:4]
+ | |--IDENT -> s4 [24:11]
+ | |--ASSIGN -> = [24:14]
+ | | `--EXPR -> EXPR [24:19]
+ | | `--DOT -> . [24:19]
+ | | |--IDENT -> STR [24:16]
+ | | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """ [24:20]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [24:23]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [24:32]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [25:11]
+ | | | `--IDENT -> s [25:11]
+ | | |--EMBEDDED_EXPRESSION_END -> } [25:13]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [25:18]
+ | | | `--IDENT -> s [25:18]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [25:14]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [25:15]
+ | | |--EMBEDDED_EXPRESSION_END -> } [25:20]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [25:25]
+ | | | `--IDENT -> s [25:25]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [25:21]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [25:22]
+ | | |--EMBEDDED_EXPRESSION_END -> } [25:27]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [25:28]
+ | | `--STRING_TEMPLATE_END -> """ [25:28]
+ | `--SEMI -> ; [25:31]
+ |--VARIABLE_DEF -> VARIABLE_DEF [27:4]
+ | |--MODIFIERS -> MODIFIERS [27:4]
+ | |--TYPE -> TYPE [27:4]
+ | | `--IDENT -> String [27:4]
+ | |--IDENT -> s5 [27:11]
+ | |--ASSIGN -> = [27:14]
+ | | `--EXPR -> EXPR [27:19]
+ | | `--DOT -> . [27:19]
+ | | |--IDENT -> STR [27:16]
+ | | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """ [27:20]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [27:23]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [27:32]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [28:10]
+ | | | `--IDENT -> s [28:10]
+ | | |--EMBEDDED_EXPRESSION_END -> } [28:11]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [28:14]
+ | | | `--IDENT -> s [28:14]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [28:12]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [28:12]
+ | | |--EMBEDDED_EXPRESSION_END -> } [28:15]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [28:18]
+ | | | `--IDENT -> s [28:18]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [28:16]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [28:16]
+ | | |--EMBEDDED_EXPRESSION_END -> } [28:19]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [28:20]
+ | | `--STRING_TEMPLATE_END -> """ [28:20]
+ | `--SEMI -> ; [28:23]
+ |--VARIABLE_DEF -> VARIABLE_DEF [30:4]
+ | |--MODIFIERS -> MODIFIERS [30:4]
+ | |--TYPE -> TYPE [30:4]
+ | | `--IDENT -> String [30:4]
+ | |--IDENT -> s6 [30:11]
+ | |--ASSIGN -> = [30:14]
+ | | `--EXPR -> EXPR [30:19]
+ | | `--DOT -> . [30:19]
+ | | |--IDENT -> STR [30:16]
+ | | `--STRING_TEMPLATE_BEGIN -> " [30:20]
+ | | |--STRING_TEMPLATE_CONTENT -> [30:21]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [30:22]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [30:24]
+ | | | `--IDENT -> s [30:24]
+ | | |--EMBEDDED_EXPRESSION_END -> } [30:25]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [30:28]
+ | | | `--IDENT -> s [30:28]
+ | | |--STRING_TEMPLATE_CONTENT -> [30:26]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [30:26]
+ | | |--EMBEDDED_EXPRESSION_END -> } [30:29]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [30:32]
+ | | | `--IDENT -> s [30:32]
+ | | |--STRING_TEMPLATE_CONTENT -> [30:30]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [30:30]
+ | | |--EMBEDDED_EXPRESSION_END -> } [30:33]
+ | | |--STRING_TEMPLATE_CONTENT -> [30:34]
+ | | `--STRING_TEMPLATE_END -> " [30:34]
+ | `--SEMI -> ; [30:35]
+ |--VARIABLE_DEF -> VARIABLE_DEF [32:4]
+ | |--MODIFIERS -> MODIFIERS [32:4]
+ | |--TYPE -> TYPE [32:4]
+ | | `--IDENT -> String [32:4]
+ | |--IDENT -> code [32:11]
+ | |--ASSIGN -> = [32:16]
+ | | `--EXPR -> EXPR [32:18]
+ | | `--TEXT_BLOCK_LITERAL_BEGIN -> """ [32:18]
+ | | |--TEXT_BLOCK_CONTENT -> \n public class Test {\n private void test(int a) {\n String s1 = TEST."p\\{a}s";\n String s2 = "p\\{a}s";\n }\n }\n [32:21]
+ | | `--TEXT_BLOCK_LITERAL_END -> """ [39:14]
+ | `--SEMI -> ; [39:17]
+ `--RCURLY -> } [40:0]
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateInlineCodeTricky.txt b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateInlineCodeTricky.txt
new file mode 100644
index 00000000000..c0d045258fd
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateInlineCodeTricky.txt
@@ -0,0 +1,218 @@
+COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
+|--PACKAGE_DEF -> package [2:0]
+| |--ANNOTATIONS -> ANNOTATIONS [2:47]
+| |--DOT -> . [2:47]
+| | |--DOT -> . [2:39]
+| | | |--DOT -> . [2:28]
+| | | | |--DOT -> . [2:22]
+| | | | | |--DOT -> . [2:11]
+| | | | | | |--IDENT -> com [2:8]
+| | | | | | `--IDENT -> puppycrawl [2:12]
+| | | | | `--IDENT -> tools [2:23]
+| | | | `--IDENT -> checkstyle [2:29]
+| | | `--IDENT -> grammar [2:40]
+| | `--IDENT -> java21 [2:48]
+| `--SEMI -> ; [2:54]
+`--CLASS_DEF -> CLASS_DEF [4:0]
+ |--MODIFIERS -> MODIFIERS [4:0]
+ | `--LITERAL_PUBLIC -> public [4:0]
+ |--LITERAL_CLASS -> class [4:7]
+ |--IDENT -> InputTextBlockTemplateInlineCodeTricky [4:13]
+ `--OBJBLOCK -> OBJBLOCK [4:52]
+ |--LCURLY -> { [4:52]
+ |--METHOD_DEF -> METHOD_DEF [5:4]
+ | |--MODIFIERS -> MODIFIERS [5:4]
+ | |--TYPE -> TYPE [5:4]
+ | | `--IDENT -> String [5:4]
+ | |--IDENT -> genSource [5:11]
+ | |--LPAREN -> ( [5:20]
+ | |--PARAMETERS -> PARAMETERS [5:21]
+ | |--RPAREN -> ) [5:21]
+ | `--SLIST -> { [5:23]
+ | |--LITERAL_RETURN -> return [6:8]
+ | | |--EXPR -> EXPR [6:18]
+ | | | `--DOT -> . [6:18]
+ | | | |--IDENT -> STR [6:15]
+ | | | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """ [6:20]
+ | | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n import java.util.FormatProcessor;\n import java.util.Locale;\n\n public class StringTemplateTest$ {\n static final FormatProcessor FMT = FormatProcessor.create(Locale.US);\n static String STR = "this is static String";\n static char C = 'c';\n static Character CHAR = 'C';\n static long L = -12345678910l;\n static Long LONG = 9876543210l;\n static int I = 42;\n static Integer INT = -49;\n static boolean BO = true;\n static Boolean BOOL = false;\n static short S = 13;\n static Short SHORT = -17;\n static byte BY = -3;\n static Byte BYTE = 12;\n static float F = 4.789f;\n static Float FLOAT = -0.000006f;\n static double D = 6545745.6734654563;\n static Double DOUBLE = -4323.7645676574;\n\n public static void run(java.util.List log) {\n runGeneral(log);\n runCharacter(log);\n runIntegral(log);\n runBigInt(log);\n runFloating(log);\n runBigFloat(log);\n runDate(log);\n }\n public static void runGeneral(java.util.List log) {\n [6:23]
+ | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [6:1443]
+ | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [40:35]
+ | | | | `--METHOD_CALL -> ( [40:35]
+ | | | | |--IDENT -> genFragments [40:23]
+ | | | | |--ELIST -> ELIST [40:44]
+ | | | | | `--EXPR -> EXPR [40:44]
+ | | | | | `--DOT -> . [40:44]
+ | | | | | |--IDENT -> Category [40:36]
+ | | | | | `--IDENT -> GENERAL [40:45]
+ | | | | `--RPAREN -> ) [40:52]
+ | | | |--EMBEDDED_EXPRESSION_END -> } [40:54]
+ | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [43:35]
+ | | | | `--METHOD_CALL -> ( [43:35]
+ | | | | |--IDENT -> genFragments [43:23]
+ | | | | |--ELIST -> ELIST [43:44]
+ | | | | | `--EXPR -> EXPR [43:44]
+ | | | | | `--DOT -> . [43:44]
+ | | | | | |--IDENT -> Category [43:36]
+ | | | | | `--IDENT -> CHARACTER [43:45]
+ | | | | `--RPAREN -> ) [43:54]
+ | | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n }\n public static void runCharacter(java.util.List log) {\n [40:55]
+ | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [40:172]
+ | | | |--EMBEDDED_EXPRESSION_END -> } [43:56]
+ | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [46:35]
+ | | | | `--METHOD_CALL -> ( [46:35]
+ | | | | |--IDENT -> genFragments [46:23]
+ | | | | |--ELIST -> ELIST [46:44]
+ | | | | | `--EXPR -> EXPR [46:44]
+ | | | | | `--DOT -> . [46:44]
+ | | | | | |--IDENT -> Category [46:36]
+ | | | | | `--IDENT -> INTEGRAL [46:45]
+ | | | | `--RPAREN -> ) [46:53]
+ | | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n }\n public static void runIntegral(java.util.List log) {\n [43:57]
+ | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [43:173]
+ | | | |--EMBEDDED_EXPRESSION_END -> } [46:55]
+ | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [49:35]
+ | | | | `--METHOD_CALL -> ( [49:35]
+ | | | | |--IDENT -> genFragments [49:23]
+ | | | | |--ELIST -> ELIST [49:44]
+ | | | | | `--EXPR -> EXPR [49:44]
+ | | | | | `--DOT -> . [49:44]
+ | | | | | |--IDENT -> Category [49:36]
+ | | | | | `--IDENT -> BIG_INT [49:45]
+ | | | | `--RPAREN -> ) [49:52]
+ | | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n }\n public static void runBigInt(java.util.List log) {\n [46:56]
+ | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [46:170]
+ | | | |--EMBEDDED_EXPRESSION_END -> } [49:54]
+ | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [52:35]
+ | | | | `--METHOD_CALL -> ( [52:35]
+ | | | | |--IDENT -> genFragments [52:23]
+ | | | | |--ELIST -> ELIST [52:44]
+ | | | | | `--EXPR -> EXPR [52:44]
+ | | | | | `--DOT -> . [52:44]
+ | | | | | |--IDENT -> Category [52:36]
+ | | | | | `--IDENT -> FLOATING [52:45]
+ | | | | `--RPAREN -> ) [52:53]
+ | | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n }\n public static void runFloating(java.util.List log) {\n [49:55]
+ | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [49:171]
+ | | | |--EMBEDDED_EXPRESSION_END -> } [52:55]
+ | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [55:35]
+ | | | | `--METHOD_CALL -> ( [55:35]
+ | | | | |--IDENT -> genFragments [55:23]
+ | | | | |--ELIST -> ELIST [55:44]
+ | | | | | `--EXPR -> EXPR [55:44]
+ | | | | | `--DOT -> . [55:44]
+ | | | | | |--IDENT -> Category [55:36]
+ | | | | | `--IDENT -> BIG_FLOAT [55:45]
+ | | | | `--RPAREN -> ) [55:54]
+ | | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n }\n public static void runBigFloat(java.util.List log) {\n [52:56]
+ | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [52:172]
+ | | | |--EMBEDDED_EXPRESSION_END -> } [55:56]
+ | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [58:35]
+ | | | | `--METHOD_CALL -> ( [58:35]
+ | | | | |--IDENT -> genFragments [58:23]
+ | | | | |--ELIST -> ELIST [58:44]
+ | | | | | `--EXPR -> EXPR [58:44]
+ | | | | | `--DOT -> . [58:44]
+ | | | | | |--IDENT -> Category [58:36]
+ | | | | | `--IDENT -> DATE [58:45]
+ | | | | `--RPAREN -> ) [58:49]
+ | | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n }\n public static void runDate(java.util.List log) {\n [55:57]
+ | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [55:169]
+ | | | |--EMBEDDED_EXPRESSION_END -> } [58:51]
+ | | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n }\n static void test(String fmt, String format, String expression,\n Object value, java.util.List log) {\n var formatted = String.format(java.util.Locale.US, format, value);\n if (!fmt.equals(formatted)) {\n log.add(" format: '%s' expression: '%s' value: '%s' expected: '%s' found:"\n + " '%s'".formatted(format, expression, value, formatted, fmt));\n }\n }\n }\n [58:52]
+ | | | `--STRING_TEMPLATE_END -> """ [58:606]
+ | | `--SEMI -> ; [69:16]
+ | `--RCURLY -> } [70:4]
+ |--VARIABLE_DEF -> VARIABLE_DEF [72:4]
+ | |--MODIFIERS -> MODIFIERS [72:4]
+ | |--TYPE -> TYPE [72:4]
+ | | `--IDENT -> String [72:4]
+ | |--IDENT -> s [72:11]
+ | |--ASSIGN -> = [72:13]
+ | | `--EXPR -> EXPR [72:18]
+ | | `--DOT -> . [72:18]
+ | | |--IDENT -> STR [72:15]
+ | | `--TEXT_BLOCK_TEMPLATE_BEGIN -> """ [72:19]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [72:22]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [72:35]
+ | | |--EMBEDDED_EXPRESSION_END -> } [73:14]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [73:15]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [73:15]
+ | | |--EMBEDDED_EXPRESSION_END -> } [73:17]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [73:25]
+ | | | `--PLUS -> + [73:25]
+ | | | |--STRING_LITERAL -> "x" [73:21]
+ | | | `--STRING_LITERAL -> "x" [73:27]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [73:18]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [73:18]
+ | | |--EMBEDDED_EXPRESSION_END -> } [73:31]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [73:39]
+ | | | `--PLUS -> + [73:39]
+ | | | |--STRING_LITERAL -> "y" [73:35]
+ | | | `--STRING_LITERAL -> "y" [73:41]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [73:32]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [73:32]
+ | | |--EMBEDDED_EXPRESSION_END -> } [73:45]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> \n [73:46]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [73:59]
+ | | |--EMBEDDED_EXPRESSION_END -> } [74:14]
+ | | |--TEXT_BLOCK_TEMPLATE_CONTENT -> [74:15]
+ | | `--STRING_TEMPLATE_END -> """ [74:15]
+ | `--SEMI -> ; [74:18]
+ |--METHOD_DEF -> METHOD_DEF [76:4]
+ | |--MODIFIERS -> MODIFIERS [76:4]
+ | | `--LITERAL_PRIVATE -> private [76:4]
+ | |--TYPE -> TYPE [76:12]
+ | | `--IDENT -> String [76:12]
+ | |--IDENT -> genFragments [76:19]
+ | |--LPAREN -> ( [76:31]
+ | |--PARAMETERS -> PARAMETERS [76:32]
+ | | `--PARAMETER_DEF -> PARAMETER_DEF [76:32]
+ | | |--MODIFIERS -> MODIFIERS [76:32]
+ | | |--TYPE -> TYPE [76:32]
+ | | | `--IDENT -> Category [76:32]
+ | | `--IDENT -> category [76:41]
+ | |--RPAREN -> ) [76:49]
+ | `--SLIST -> { [76:51]
+ | |--LITERAL_RETURN -> return [77:8]
+ | | |--EXPR -> EXPR [77:15]
+ | | | `--STRING_LITERAL -> "whatever" [77:15]
+ | | `--SEMI -> ; [77:25]
+ | `--RCURLY -> } [78:4]
+ |--ENUM_DEF -> ENUM_DEF [80:4]
+ | |--MODIFIERS -> MODIFIERS [80:4]
+ | | |--LITERAL_PRIVATE -> private [80:4]
+ | | `--LITERAL_STATIC -> static [80:12]
+ | |--ENUM -> enum [80:19]
+ | |--IDENT -> Category [80:24]
+ | `--OBJBLOCK -> OBJBLOCK [80:33]
+ | |--LCURLY -> { [80:33]
+ | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [81:8]
+ | | |--ANNOTATIONS -> ANNOTATIONS [81:8]
+ | | `--IDENT -> GENERAL [81:8]
+ | |--COMMA -> , [81:15]
+ | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [81:17]
+ | | |--ANNOTATIONS -> ANNOTATIONS [81:17]
+ | | `--IDENT -> CHARACTER [81:17]
+ | |--COMMA -> , [81:26]
+ | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [81:28]
+ | | |--ANNOTATIONS -> ANNOTATIONS [81:28]
+ | | `--IDENT -> INTEGRAL [81:28]
+ | |--COMMA -> , [81:36]
+ | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [81:38]
+ | | |--ANNOTATIONS -> ANNOTATIONS [81:38]
+ | | `--IDENT -> BIG_INT [81:38]
+ | |--COMMA -> , [81:45]
+ | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [81:47]
+ | | |--ANNOTATIONS -> ANNOTATIONS [81:47]
+ | | `--IDENT -> FLOATING [81:47]
+ | |--COMMA -> , [81:55]
+ | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [81:57]
+ | | |--ANNOTATIONS -> ANNOTATIONS [81:57]
+ | | `--IDENT -> BIG_FLOAT [81:57]
+ | |--COMMA -> , [81:66]
+ | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [81:68]
+ | | |--ANNOTATIONS -> ANNOTATIONS [81:68]
+ | | `--IDENT -> DATE [81:68]
+ | |--SEMI -> ; [81:72]
+ | `--RCURLY -> } [82:4]
+ `--RCURLY -> } [83:0]
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockParsingFail.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockParsingFail.java
new file mode 100644
index 00000000000..7d714c2de04
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockParsingFail.java
@@ -0,0 +1,12 @@
+// not compilable with javac, this is a test for parsing failure
+package com.puppycrawl.tools.checkstyle.grammar.java21;
+
+public class InputTextBlockParsingFail {
+
+ // This should fail to parse since it is not
+ // compilable with javac. Our grammar previously
+ // accepted this, but it should not.
+ String s = """
+ \{}
+ """;
+}
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateBasic.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateBasic.java
new file mode 100644
index 00000000000..1bc2ac8b5b3
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateBasic.java
@@ -0,0 +1,40 @@
+//non-compiled with javac: Compilable with Java21
+package com.puppycrawl.tools.checkstyle.grammar.java21;
+
+public class InputTextBlockTemplateBasic {
+
+ String s = "my string";
+
+ String s1 = STR."""
+ """;
+ String s2 = STR."""
+ \{
+ s
+ }
+ """;
+ String s3 = STR."""
+ \{
+ s
+ }
+ \{
+ s
+ }
+ """;
+
+ String s4 = STR."""
+ \{ s } \{ s } \{ s }""";
+
+ String s5 = STR."""
+ \{s}\{s}\{s}""";
+
+ String s6 = STR." \{s}\{s}\{s}";
+
+ String code = """
+ public class Test {
+ private void test(int a) {
+ String s1 = TEST."p\\{a}s";
+ String s2 = "p\\{a}s";
+ }
+ }
+ """;
+}
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateInlineCodeTricky.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateInlineCodeTricky.java
new file mode 100644
index 00000000000..14c38d4f561
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateInlineCodeTricky.java
@@ -0,0 +1,83 @@
+//non-compiled with javac: Compilable with Java21
+package com.puppycrawl.tools.checkstyle.grammar.java21;
+
+public class InputTextBlockTemplateInlineCodeTricky {
+ String genSource() {
+ return STR. """
+ import java.util.FormatProcessor;
+ import java.util.Locale;
+
+ public class StringTemplateTest$ {
+ static final FormatProcessor FMT = FormatProcessor.create(Locale.US);
+ static String STR = "this is static String";
+ static char C = 'c';
+ static Character CHAR = 'C';
+ static long L = -12345678910l;
+ static Long LONG = 9876543210l;
+ static int I = 42;
+ static Integer INT = -49;
+ static boolean BO = true;
+ static Boolean BOOL = false;
+ static short S = 13;
+ static Short SHORT = -17;
+ static byte BY = -3;
+ static Byte BYTE = 12;
+ static float F = 4.789f;
+ static Float FLOAT = -0.000006f;
+ static double D = 6545745.6734654563;
+ static Double DOUBLE = -4323.7645676574;
+
+ public static void run(java.util.List log) {
+ runGeneral(log);
+ runCharacter(log);
+ runIntegral(log);
+ runBigInt(log);
+ runFloating(log);
+ runBigFloat(log);
+ runDate(log);
+ }
+ public static void runGeneral(java.util.List log) {
+ \{ genFragments(Category.GENERAL) }
+ }
+ public static void runCharacter(java.util.List log) {
+ \{ genFragments(Category.CHARACTER) }
+ }
+ public static void runIntegral(java.util.List log) {
+ \{ genFragments(Category.INTEGRAL) }
+ }
+ public static void runBigInt(java.util.List log) {
+ \{ genFragments(Category.BIG_INT) }
+ }
+ public static void runFloating(java.util.List log) {
+ \{ genFragments(Category.FLOATING) }
+ }
+ public static void runBigFloat(java.util.List log) {
+ \{ genFragments(Category.BIG_FLOAT) }
+ }
+ public static void runDate(java.util.List log) {
+ \{ genFragments(Category.DATE) }
+ }
+ static void test(String fmt, String format, String expression,
+ Object value, java.util.List log) {
+ var formatted = String.format(java.util.Locale.US, format, value);
+ if (!fmt.equals(formatted)) {
+ log.add(" format: '%s' expression: '%s' value: '%s' expected: '%s' found:"
+ + " '%s'".formatted(format, expression, value, formatted, fmt));
+ }
+ }
+ }
+ """ ;
+ }
+
+ String s = STR."""
+ \{}\{}\{ "x" + "x" }\{ "y" + "y" }
+ \{}""";
+
+ private String genFragments(Category category) {
+ return "whatever";
+ }
+
+ private static enum Category {
+ GENERAL, CHARACTER, INTEGRAL, BIG_INT, FLOATING, BIG_FLOAT, DATE;
+ }
+}