From d875e8f78d513757a62c13c5b8b7d73c96e3c327 Mon Sep 17 00:00:00 2001 From: Nick Mancuso Date: Tue, 30 Jan 2024 08:38:59 -0500 Subject: [PATCH] Issue #14195: Support Java 21 String Template Syntax (Text Blocks) --- .../projects-to-test/openjdk21-excluded.files | 8 - pom.xml | 4 +- .../tools/checkstyle/JavaAstVisitor.java | 168 ++++++++++++++ .../tools/checkstyle/api/TokenTypes.java | 147 ++++++++++++ .../grammar/java/JavaLanguageLexer.g4 | 19 +- .../grammar/java/JavaLanguageParser.g4 | 15 +- .../tools/checkstyle/JavaAstVisitorTest.java | 5 +- .../coding/IllegalTokenTextCheckTest.java | 5 +- .../grammar/GeneratedJavaTokenTypesTest.java | 14 +- .../java21/Java21AstRegressionTest.java | 22 +- .../tools/checkstyle/utils/TokenUtilTest.java | 6 +- .../java21/ExpectedTextBlockTemplateBasic.txt | 181 +++++++++++++++ ...ectedTextBlockTemplateInlineCodeTricky.txt | 218 ++++++++++++++++++ .../java21/InputTextBlockParsingFail.java | 12 + .../java21/InputTextBlockTemplateBasic.java | 40 ++++ ...nputTextBlockTemplateInlineCodeTricky.java | 83 +++++++ 16 files changed, 918 insertions(+), 29 deletions(-) create mode 100644 src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateBasic.txt create mode 100644 src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateInlineCodeTricky.txt create mode 100644 src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockParsingFail.java create mode 100644 src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateBasic.java create mode 100644 src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateInlineCodeTricky.java 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; + } +}