From c497d9a6a1bcaca0bafa86028ae136b84d7e83ce 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 - .../tools/checkstyle/JavaAstVisitor.java | 150 +++++++++++++++ .../tools/checkstyle/api/TokenTypes.java | 18 ++ .../grammar/java/JavaLanguageLexer.g4 | 95 ++++++--- .../grammar/java/JavaLanguageParser.g4 | 18 +- .../tools/checkstyle/JavaAstVisitorTest.java | 5 +- .../coding/IllegalTokenTextCheckTest.java | 5 +- .../grammar/GeneratedJavaTokenTypesTest.java | 14 +- .../java21/Java21AstRegressionTest.java | 51 +++++ .../tools/checkstyle/utils/TokenUtilTest.java | 6 +- .../java21/ExpectedTextBlockTemplateBasic.txt | 181 +++++++++++++++++ ...ectedTextBlockTemplateInlineCodeTricky.txt | 182 ++++++++++++++++++ .../java21/InputTextBlockParsingFail.java | 12 ++ .../java21/InputTextBlockTemplateBasic.java | 40 ++++ ...nputTextBlockTemplateInlineCodeTricky.java | 77 ++++++++ 15 files changed, 813 insertions(+), 49 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 2ad88fa5498..0264e7b30d6 100644 --- a/config/projects-to-test/openjdk21-excluded.files +++ b/config/projects-to-test/openjdk21-excluded.files @@ -29,14 +29,6 @@ - - - - - - - - diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java b/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java index 958134a6dd2..8cfa5427f58 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; + } + + 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; + } + + 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; + } + + 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 1801bdd5c07..8c36af47bb2 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/api/TokenTypes.java @@ -6783,6 +6783,24 @@ public final class TokenTypes { public static final int UNNAMED_PATTERN_DEF = JavaLanguageLexer.UNNAMED_PATTERN_DEF; + /** + * + */ + public static final int TEXT_BLOCK_TEMPLATE_BEGIN = + JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_BEGIN; + + /** + * + */ + public static final int TEXT_BLOCK_TEMPLATE_END = + JavaLanguageLexer.TEXT_BLOCK_TEMPLATE_END; + + /** + * + */ + 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 6acaec69dd1..097913e7bff 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 { @@ -156,6 +157,8 @@ import com.puppycrawl.tools.checkstyle.grammar.CrAwareLexerSimulator; int startCol = -1; private int stringTemplateDepth = 0; + + private int textBlockTemplateDepth = 0; } // Keywords and restricted identifiers @@ -321,11 +324,12 @@ AT: '@'; ELLIPSIS: '...'; // String templates -STRING_TEMPLATE_BEGIN: '"' StringFragment '\\' '{' +STRING_TEMPLATE_BEGIN: '"' StringFragment '\\' '{' {stringTemplateDepth++;} ; -STRING_TEMPLATE_MID: {stringTemplateDepth > 0}? +// this is also used for text block template middles that have no content +STRING_TEMPLATE_MID: {stringTemplateDepth > 0 || textBlockTemplateDepth > 0}? '}' StringFragment '\\' '{' ; @@ -334,6 +338,64 @@ STRING_TEMPLATE_END: {stringTemplateDepth > 0}? {stringTemplateDepth--;} ; +// Text Block Templates + +TEXT_BLOCK_TEMPLATE_BEGIN: '"' '"' '"' TextBlockContent ~'\\' '\\' '{' + {textBlockTemplateDepth++;} + ; + +// We do not make TextBlockTemplateContent optional here, because this token +// would then match the STRING_TEMPLATE_MID token above when there is no content. +// +// Example: +// String s = STR.""" +// \{}\{}"""; +// ^ this (`}\{`) is matched by STRING_TEMPLATE_MID +// +// In order to make this token work for these lexemes, we would need to add +// more semantic predicates to both tokens, +// which would be more complex than just reusing the STRING_TEMPLATE_MID token. +TEXT_BLOCK_TEMPLATE_MID: {textBlockTemplateDepth > 0}? + '}' TextBlockContent ~'\\' '\\' '{' + ; + +TEXT_BLOCK_TEMPLATE_END: {textBlockTemplateDepth > 0}? + '}' TextBlockContent? '"' '"' '"' + {textBlockTemplateDepth--;} + ; + +// Text block fragments + +fragment TextBlockContent + : ( TwoDoubleQuotes + | OneDoubleQuote + | Newline + | TextBlockCharacter + )+ + ; + +fragment TextBlockCharacter + : ~["\\] + | TextBlockStandardEscape + | EscapeSequence + ; + +fragment TextBlockStandardEscape + : '\\' ( [btnfrs"'\\] | Newline | OneDoubleQuote ) + ; + +fragment Newline + : '\n' | '\r' ('\n')? + ; + +fragment TwoDoubleQuotes + : '"''"' ( Newline | ~'"' ) + ; + +fragment OneDoubleQuote + : '"' ( Newline | ~'"' ) + ; + // Whitespace and comments WS: [ \t\r\n\u000C]+ -> skip; @@ -425,32 +487,9 @@ fragment Letter // Text block lexical mode mode TextBlock; - TEXT_BLOCK_CONTENT - : ( TwoDoubleQuotes - | OneDoubleQuote - | Newline - | ~'"' - | TextBlockStandardEscape - )+ - ; + + TEXT_BLOCK_CONTENT: TextBlockContent; TEXT_BLOCK_LITERAL_END : '"' '"' '"' -> popMode ; - - // Text block fragment rules - fragment TextBlockStandardEscape - : '\\' [btnfrs"'\\] - ; - - fragment Newline - : '\n' | '\r' ('\n')? - ; - - fragment TwoDoubleQuotes - : '"''"' ( Newline | ~'"' ) - ; - - fragment OneDoubleQuote - : '"' ( Newline | ~'"' ) - ; 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..e3b97c7a976 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,31 @@ 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 ; +// 'isEmptyTextBlockMiddle' is used to distinguish between empty string and empty text block +// middle. This is necessary because the lexer cannot distinguish between the two. stringTemplateMiddle : STRING_TEMPLATE_MID expr? ; +textBlockTemplateMiddle + : middle=STRING_TEMPLATE_MID expr? + | 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 14bced10e26..ae0be85aa05 100644 --- a/src/test/java/com/puppycrawl/tools/checkstyle/JavaAstVisitorTest.java +++ b/src/test/java/com/puppycrawl/tools/checkstyle/JavaAstVisitorTest.java @@ -88,10 +88,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 9bfed691c4c..d3f5f674b3a 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 @@ -19,9 +19,16 @@ package com.puppycrawl.tools.checkstyle.grammar.java21; +import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertThrows; + +import java.io.File; + import org.junit.jupiter.api.Test; import com.puppycrawl.tools.checkstyle.AbstractTreeTestSupport; +import com.puppycrawl.tools.checkstyle.JavaParser; +import com.puppycrawl.tools.checkstyle.api.CheckstyleException; public class Java21AstRegressionTest extends AbstractTreeTestSupport { @@ -72,4 +79,48 @@ public void testUnnamedVariableSwitch() throws Exception { "InputUnnamedVariableSwitch.java")); } + @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")); + } + + @Test + public void testTextBlockParsingFail() throws Exception { + final File file = + new File(getNonCompilablePath("InputTextBlockParsingFail.java")); + + final Throwable throwable = + assertThrows("Exception should be thrown due to parsing failure.", + CheckstyleException.class, + () -> JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS) + ); + + final String incorrectThrowableCauseMessage = + "Cause of CheckstyleException should be IllegalStateException."; + + assertWithMessage(incorrectThrowableCauseMessage) + .that(throwable.getCause()) + .isInstanceOf(IllegalStateException.class); + + final String incorrectParsingFailureMessage = + "Message of IllegalStateException should contain the parsing failure."; + + assertWithMessage(incorrectParsingFailureMessage) + .that(throwable.getCause().getMessage()) + .contains("9:15: no viable alternative at input '\"\"\"\\n \\{'"); + + } } 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..2fcba4db1ec --- /dev/null +++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedTextBlockTemplateInlineCodeTricky.txt @@ -0,0 +1,182 @@ +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, 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: '%s'".formatted(format, expression, value, formatted, fmt));\n }\n }\n }\n [58:52] + | | | `--STRING_TEMPLATE_END -> """ [58:557] + | | `--SEMI -> ; [67:16] + | `--RCURLY -> } [68:4] + |--METHOD_DEF -> METHOD_DEF [70:4] + | |--MODIFIERS -> MODIFIERS [70:4] + | | `--LITERAL_PRIVATE -> private [70:4] + | |--TYPE -> TYPE [70:12] + | | `--IDENT -> String [70:12] + | |--IDENT -> genFragments [70:19] + | |--LPAREN -> ( [70:31] + | |--PARAMETERS -> PARAMETERS [70:32] + | | `--PARAMETER_DEF -> PARAMETER_DEF [70:32] + | | |--MODIFIERS -> MODIFIERS [70:32] + | | |--TYPE -> TYPE [70:32] + | | | `--IDENT -> Category [70:32] + | | `--IDENT -> category [70:41] + | |--RPAREN -> ) [70:49] + | `--SLIST -> { [70:51] + | |--LITERAL_RETURN -> return [71:8] + | | |--EXPR -> EXPR [71:15] + | | | `--STRING_LITERAL -> "whatever" [71:15] + | | `--SEMI -> ; [71:25] + | `--RCURLY -> } [72:4] + |--ENUM_DEF -> ENUM_DEF [74:4] + | |--MODIFIERS -> MODIFIERS [74:4] + | | |--LITERAL_PRIVATE -> private [74:4] + | | `--LITERAL_STATIC -> static [74:12] + | |--ENUM -> enum [74:19] + | |--IDENT -> Category [74:24] + | `--OBJBLOCK -> OBJBLOCK [74:33] + | |--LCURLY -> { [74:33] + | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [75:8] + | | |--ANNOTATIONS -> ANNOTATIONS [75:8] + | | `--IDENT -> GENERAL [75:8] + | |--COMMA -> , [75:15] + | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [75:17] + | | |--ANNOTATIONS -> ANNOTATIONS [75:17] + | | `--IDENT -> CHARACTER [75:17] + | |--COMMA -> , [75:26] + | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [75:28] + | | |--ANNOTATIONS -> ANNOTATIONS [75:28] + | | `--IDENT -> INTEGRAL [75:28] + | |--COMMA -> , [75:36] + | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [75:38] + | | |--ANNOTATIONS -> ANNOTATIONS [75:38] + | | `--IDENT -> BIG_INT [75:38] + | |--COMMA -> , [75:45] + | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [75:47] + | | |--ANNOTATIONS -> ANNOTATIONS [75:47] + | | `--IDENT -> FLOATING [75:47] + | |--COMMA -> , [75:55] + | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [75:57] + | | |--ANNOTATIONS -> ANNOTATIONS [75:57] + | | `--IDENT -> BIG_FLOAT [75:57] + | |--COMMA -> , [75:66] + | |--ENUM_CONSTANT_DEF -> ENUM_CONSTANT_DEF [75:68] + | | |--ANNOTATIONS -> ANNOTATIONS [75:68] + | | `--IDENT -> DATE [75:68] + | |--SEMI -> ; [75:72] + | `--RCURLY -> } [76:4] + `--RCURLY -> } [77: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..2a4d1c0f254 --- /dev/null +++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputTextBlockTemplateInlineCodeTricky.java @@ -0,0 +1,77 @@ +//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)); + } + } + } + """ ; + } + + private String genFragments(Category category) { + return "whatever"; + } + + private static enum Category { + GENERAL, CHARACTER, INTEGRAL, BIG_INT, FLOATING, BIG_FLOAT, DATE; + } +}