diff --git a/config/checker-framework-suppressions/checker-nullness-optional-interning-suppressions.xml b/config/checker-framework-suppressions/checker-nullness-optional-interning-suppressions.xml
index 200241965a2..1565d4fdd3c 100644
--- a/config/checker-framework-suppressions/checker-nullness-optional-interning-suppressions.xml
+++ b/config/checker-framework-suppressions/checker-nullness-optional-interning-suppressions.xml
@@ -5358,6 +5358,13 @@
+
+ src/main/java/com/puppycrawl/tools/checkstyle/grammar/CompositeLexerContextCache.java
+ dereference.of.nullable
+ dereference of possibly-null reference currentContext
+ if (currentContext.getCurlyBraceDepth() == 0) {
+
+
src/main/java/com/puppycrawl/tools/checkstyle/gui/BaseCellEditor.java
return
diff --git a/config/import-control.xml b/config/import-control.xml
index acf6a101c24..4a4b61362bf 100644
--- a/config/import-control.xml
+++ b/config/import-control.xml
@@ -307,6 +307,12 @@
+
+
+
+
+
+
diff --git a/config/linkcheck-suppressions.txt b/config/linkcheck-suppressions.txt
index 8030a07f70c..00921c6958b 100644
--- a/config/linkcheck-suppressions.txt
+++ b/config/linkcheck-suppressions.txt
@@ -1341,3 +1341,7 @@
com/puppycrawl/tools/checkstyle/xpath/iterators/ReverseListIterator.html#%3Cinit%3E(java.util.Collection): doesn't exist.
com/puppycrawl/tools/checkstyle/utils/UnmodifiableCollectionUtil.html#copyOfArray(T%5B%5D,int): doesn't exist.
com/puppycrawl/tools/checkstyle/site/XdocsTemplateSink.CustomPrintWriter.html#%3Cinit%3E(java.io.Writer): doesn't exist.
+com/puppycrawl/tools/checkstyle/grammar/CompositeLexerContextCache.html#%3Cinit%3E(org.antlr.v4.runtime.Lexer): doesn't exist.
+com/puppycrawl/tools/checkstyle/grammar/CompositeLexerContextCache.TemplateContext.html#%3Cinit%3E(int,int): doesn't exist.
+#%3Cinit%3E(org.antlr.v4.runtime.Lexer): doesn't exist.
+#%3Cinit%3E(int,int): doesn't exist.
diff --git a/pom.xml b/pom.xml
index 3ab7a4eca56..9e80811b499 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4362,6 +4362,7 @@
com.puppycrawl.tools.checkstyle.Checker*
com.puppycrawl.tools.checkstyle.ThreadModeSettings*
com.puppycrawl.tools.checkstyle.grammar.CrAwareLexerSimulator*
+ com.puppycrawl.tools.checkstyle.grammar.CompositeLexerContextCache*
com.puppycrawl.tools.checkstyle.AuditEventFormatter*
com.puppycrawl.tools.checkstyle.XdocsPropertyType*
@@ -4393,6 +4394,8 @@
com.puppycrawl.tools.checkstyle.ThreadModeSettingsTest
com.puppycrawl.tools.checkstyle.grammar.CrAwareLexerSimulatorTest
com.puppycrawl.tools.checkstyle.grammar.javadoc.JavadocParseTreeTest
+
+ com.puppycrawl.tools.checkstyle.grammar.java21.Java21AstRegressionTest
com.puppycrawl.tools.checkstyle.filefilters.BeforeExecutionExclusionFileFilterTest
com.puppycrawl.tools.checkstyle.checks.TranslationCheckTest
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java b/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java
index 958134a6dd2..69e216c31bd 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/JavaAstVisitor.java
@@ -1808,164 +1808,84 @@ private static DetailAstImpl buildSimpleStringTemplateArgument(
@Override
public DetailAstImpl visitStringTemplate(JavaLanguageParser.StringTemplateContext ctx) {
- final DetailAstImpl begin = buildStringTemplateBeginning(ctx);
-
- final Optional 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);
- }
+ final DetailAstImpl stringTemplateBegin = visit(ctx.stringTemplateBegin());
+
+ Optional.ofNullable(ctx.expr())
+ .map(this::visit)
+ .ifPresent(expression -> {
+ final DetailAstImpl imaginaryExpression =
+ createImaginary(TokenTypes.EMBEDDED_EXPRESSION);
+ imaginaryExpression.addChild(expression);
+ stringTemplateBegin.addChild(imaginaryExpression);
+ });
ctx.stringTemplateMiddle().stream()
.map(this::buildStringTemplateMiddle)
- .collect(Collectors.toUnmodifiableList())
- .forEach(begin::addChild);
-
- final DetailAstImpl end = buildStringTemplateEnd(ctx);
- begin.addChild(end);
- return begin;
- }
+ .forEach(stringTemplateBegin::addChild);
- /**
- * Builds the beginning of a string template AST.
- *
- * @param ctx the StringTemplateContext to build AST from
- * @return string template AST
- */
- private static DetailAstImpl buildStringTemplateBeginning(
- JavaLanguageParser.StringTemplateContext ctx) {
-
- // token looks like '"' StringFragment '\{'
- final TerminalNode context = ctx.STRING_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 stringTemplateBegin = createImaginary(
- TokenTypes.STRING_TEMPLATE_BEGIN, QUOTE,
- tokenLineNumber, tokenStartIndex
- );
-
- // remove delimiters '"' and '\{'
- final String stringFragment = tokenText.substring(
- QUOTE.length(), tokenTextLength - EMBEDDED_EXPRESSION_BEGIN.length());
-
- final DetailAstImpl stringTemplateContent = createImaginary(
- TokenTypes.STRING_TEMPLATE_CONTENT, stringFragment,
- tokenLineNumber, tokenStartIndex + QUOTE.length()
- );
- stringTemplateBegin.addChild(stringTemplateContent);
-
- final DetailAstImpl embeddedBegin = createImaginary(
- TokenTypes.EMBEDDED_EXPRESSION_BEGIN, EMBEDDED_EXPRESSION_BEGIN,
- tokenLineNumber,
- tokenStartIndex + tokenTextLength - EMBEDDED_EXPRESSION_BEGIN.length()
- );
- stringTemplateBegin.addChild(embeddedBegin);
+ final DetailAstImpl stringTemplateEnd = visit(ctx.stringTemplateEnd());
+ stringTemplateBegin.addChild(stringTemplateEnd);
return stringTemplateBegin;
}
/**
- * Builds the middle of a string template AST.
+ * Builds a string template middle AST.
*
- * @param middleContext the StringTemplateMiddleContext to build AST from
+ * @param ctx the StringTemplateMiddleContext to build AST from
* @return DetailAstImpl of string template middle
*/
private DetailAstImpl buildStringTemplateMiddle(
- JavaLanguageParser.StringTemplateMiddleContext middleContext) {
-
- // token looks like '}' StringFragment '\{'
- final TerminalNode context = middleContext.STRING_TEMPLATE_MID();
- final Token token = context.getSymbol();
- final int tokenStartIndex = token.getCharPositionInLine();
- final int tokenLineNumber = token.getLine();
- final String tokenText = context.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.STRING_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.expr())
- .map(this::visit);
-
- if (embeddedExpression.isPresent()) {
- final DetailAstImpl imaginaryExpr =
- createImaginary(TokenTypes.EMBEDDED_EXPRESSION);
- imaginaryExpr.addChild(embeddedExpression.orElseThrow());
- embeddedExpressionEnd.addNextSibling(imaginaryExpr);
- }
+ JavaLanguageParser.StringTemplateMiddleContext ctx) {
+ final DetailAstImpl stringTemplateMiddle =
+ visit(ctx.stringTemplateMid());
+
+ Optional.ofNullable(ctx.expr())
+ .map(this::visit)
+ .ifPresent(expression -> {
+ final DetailAstImpl imaginaryExpression =
+ createImaginary(TokenTypes.EMBEDDED_EXPRESSION);
+ imaginaryExpression.addChild(expression);
+ addLastSibling(stringTemplateMiddle, imaginaryExpression);
+ });
+
+ return stringTemplateMiddle;
+ }
+
+ @Override
+ public DetailAstImpl visitStringTemplateBegin(
+ JavaLanguageParser.StringTemplateBeginContext ctx) {
+ final DetailAstImpl stringTemplateBegin =
+ create(ctx.STRING_TEMPLATE_BEGIN());
+ final Optional stringTemplateContent =
+ Optional.ofNullable(ctx.STRING_TEMPLATE_CONTENT())
+ .map(this::create);
+ stringTemplateContent.ifPresent(stringTemplateBegin::addChild);
+ final DetailAstImpl embeddedExpressionBegin = create(ctx.EMBEDDED_EXPRESSION_BEGIN());
+ stringTemplateBegin.addChild(embeddedExpressionBegin);
+ return stringTemplateBegin;
+ }
+ @Override
+ public DetailAstImpl visitStringTemplateMid(JavaLanguageParser.StringTemplateMidContext ctx) {
+ final DetailAstImpl embeddedExpressionEnd = create(ctx.EMBEDDED_EXPRESSION_END());
+ final Optional stringTemplateContent =
+ Optional.ofNullable(ctx.STRING_TEMPLATE_CONTENT())
+ .map(this::create);
+ stringTemplateContent.ifPresent(self -> addLastSibling(embeddedExpressionEnd, self));
+ final DetailAstImpl embeddedExpressionBegin = create(ctx.EMBEDDED_EXPRESSION_BEGIN());
+ addLastSibling(embeddedExpressionEnd, embeddedExpressionBegin);
return embeddedExpressionEnd;
}
- /**
- * Builds the end of a string template AST.
- *
- * @param ctx the StringTemplateContext to build AST from
- * @return DetailAstImpl of string template end
- */
- private static DetailAstImpl buildStringTemplateEnd(
- JavaLanguageParser.StringTemplateContext ctx) {
-
- // token looks like '}' StringFragment '"'
- final TerminalNode context = ctx.STRING_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 - QUOTE.length()
- );
-
- final DetailAstImpl endContent = createImaginary(
- TokenTypes.STRING_TEMPLATE_CONTENT, stringFragment,
- tokenLineNumber,
- tokenStartIndex + EMBEDDED_EXPRESSION_END.length()
- );
- embeddedExpressionEnd.addNextSibling(endContent);
-
- final DetailAstImpl stringTemplateEnd = createImaginary(
- TokenTypes.STRING_TEMPLATE_END, QUOTE,
- tokenLineNumber,
- tokenStartIndex + tokenTextLength - QUOTE.length()
- );
- endContent.addNextSibling(stringTemplateEnd);
+ @Override
+ public DetailAstImpl visitStringTemplateEnd(JavaLanguageParser.StringTemplateEndContext ctx) {
+ final DetailAstImpl embeddedExpressionEnd = create(ctx.EMBEDDED_EXPRESSION_END());
+ final Optional stringTemplateContent =
+ Optional.ofNullable(ctx.STRING_TEMPLATE_CONTENT())
+ .map(this::create);
+ stringTemplateContent.ifPresent(self -> addLastSibling(embeddedExpressionEnd, self));
+ final DetailAstImpl stringTemplateEnd = create(ctx.STRING_TEMPLATE_END());
+ addLastSibling(embeddedExpressionEnd, stringTemplateEnd);
return embeddedExpressionEnd;
}
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/JavaParser.java b/src/main/java/com/puppycrawl/tools/checkstyle/JavaParser.java
index 518db6a0c13..e56c35d8c9e 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/JavaParser.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/JavaParser.java
@@ -40,6 +40,7 @@
import com.puppycrawl.tools.checkstyle.api.FileContents;
import com.puppycrawl.tools.checkstyle.api.FileText;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+import com.puppycrawl.tools.checkstyle.grammar.CompositeLexerContextCache;
import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser;
import com.puppycrawl.tools.checkstyle.utils.ParserUtil;
@@ -84,7 +85,9 @@ public static DetailAST parse(FileContents contents)
final String fullText = contents.getText().getFullText().toString();
final CharStream codePointCharStream = CharStreams.fromString(fullText);
final JavaLanguageLexer lexer = new JavaLanguageLexer(codePointCharStream, true);
+ final CompositeLexerContextCache contextCache = new CompositeLexerContextCache(lexer);
lexer.setCommentListener(contents);
+ lexer.setContextCache(contextCache);
final CommonTokenStream tokenStream = new CommonTokenStream(lexer);
final JavaLanguageParser parser =
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/grammar/CompositeLexerContextCache.java b/src/main/java/com/puppycrawl/tools/checkstyle/grammar/CompositeLexerContextCache.java
new file mode 100644
index 00000000000..e9657cc44e3
--- /dev/null
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/grammar/CompositeLexerContextCache.java
@@ -0,0 +1,157 @@
+///////////////////////////////////////////////////////////////////////////////////////////////
+// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
+// Copyright (C) 2001-2024 the original author or authors.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+package com.puppycrawl.tools.checkstyle.grammar;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import org.antlr.v4.runtime.Lexer;
+
+import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
+
+/**
+ * This class is used to keep track of the lexer context to help us determine
+ * when to switch lexer modes.
+ */
+public final class CompositeLexerContextCache {
+
+ /** Stack for tracking string template contexts. */
+ private final Deque templateContextStack;
+
+ /** The lexer to use. */
+ private final Lexer lexer;
+
+ /**
+ * Creates a new CompositeLexerContextCache instance.
+ *
+ * @param lexer the lexer to use
+ */
+ public CompositeLexerContextCache(Lexer lexer) {
+ templateContextStack = new ArrayDeque<>();
+ this.lexer = lexer;
+ }
+
+ /**
+ * Update the left curly brace context if we are in a string template.
+ */
+ public void updateLeftCurlyBraceContext() {
+ if (isInTemplateContext()) {
+ final TemplateContext currentContext = templateContextStack.pop();
+ final TemplateContext newContext = new TemplateContext(currentContext.getMode(),
+ currentContext.getCurlyBraceDepth() + 1);
+ templateContextStack.push(newContext);
+ }
+ }
+
+ /**
+ * Update the right curly brace context if we are in a string template.
+ */
+ public void updateRightCurlyBraceContext() {
+ if (isInTemplateContext()) {
+ final TemplateContext currentContext = templateContextStack.peek();
+ if (currentContext.getCurlyBraceDepth() == 0) {
+ // This right curly brace is the start delimiter
+ // of a template middle or end. We consume
+ // the right curly brace to be used as the first token
+ // in the appropriate lexer mode rule, enter
+ // the corresponding lexer mode, and keep consuming
+ // the rest of the template middle or end.
+ lexer.setType(JavaLanguageLexer.EMBEDDED_EXPRESSION_END);
+ lexer.pushMode(currentContext.getMode());
+ }
+ else {
+ // We've consumed a right curly brace within an embedded expression.
+ final TemplateContext newContext = new TemplateContext(currentContext.getMode(),
+ currentContext.getCurlyBraceDepth() - 1);
+ templateContextStack.push(newContext);
+ }
+ }
+ }
+
+ /**
+ * Enter a string template context.
+ *
+ * @param mode the lexer mode to enter
+ */
+ public void enterTemplateContext(int mode) {
+ final TemplateContext newContext = new TemplateContext(mode, 0);
+ templateContextStack.push(newContext);
+ lexer.pushMode(mode);
+ }
+
+ /**
+ * Exit a string template context.
+ */
+ public void exitTemplateContext() {
+ templateContextStack.pop();
+ lexer.popMode();
+ }
+
+ /**
+ * Check if we are in a string template context.
+ *
+ * @return true if we are in a string template context
+ */
+ private boolean isInTemplateContext() {
+ return !templateContextStack.isEmpty();
+ }
+
+ /**
+ * A class to represent the context of a string template.
+ */
+ private static final class TemplateContext {
+
+ /** The lexer mode of this context. */
+ private final int mode;
+
+ /** The depth of this context. */
+ private final int curlyBraceDepth;
+
+ /**
+ * Creates a new TemplateContext instance.
+ *
+ * @param mode the lexer mode of this context
+ * @param curlyBraceDepth the depth of this context
+ */
+ private TemplateContext(int mode, int curlyBraceDepth) {
+ this.mode = mode;
+ this.curlyBraceDepth = curlyBraceDepth;
+ }
+
+ /**
+ * Get the lexer mode of this context.
+ *
+ * @return current lexer mode
+ */
+ private int getMode() {
+ return mode;
+ }
+
+ /**
+ * Current depth of this context.
+ *
+ * @return current depth
+ */
+ private int getCurlyBraceDepth() {
+ return curlyBraceDepth;
+ }
+ }
+
+}
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 ed456f61ae6..4e9e0a0d5ab 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
@@ -121,6 +121,7 @@ tokens {
@header {
import com.puppycrawl.tools.checkstyle.grammar.CommentListener;
+import com.puppycrawl.tools.checkstyle.grammar.CompositeLexerContextCache;
import com.puppycrawl.tools.checkstyle.grammar.CrAwareLexerSimulator;
}
@@ -139,23 +140,27 @@ import com.puppycrawl.tools.checkstyle.grammar.CrAwareLexerSimulator;
}
private CommentListener commentListener = null;
+ private CompositeLexerContextCache contextCache = null;
/**
* Sets the CommentListener for the lexer.
*
* @param commentListener the commentListener to use in this lexer
*/
- public void setCommentListener(CommentListener commentListener){
+ public void setCommentListener(CommentListener commentListener) {
this.commentListener = commentListener;
}
+ public void setContextCache(CompositeLexerContextCache contextCache) {
+ this.contextCache = contextCache;
+ }
+
/** Tracks the starting line of a block comment. */
int startLine = -1;
/** Tracks the starting column of a block comment. */
int startCol = -1;
- private int stringTemplateDepth = 0;
}
// Keywords and restricted identifiers
@@ -249,9 +254,14 @@ LITERAL_FALSE: 'false';
CHAR_LITERAL: '\'' (EscapeSequence | ~['\\\r\n]) '\'';
-fragment StringFragment: (EscapeSequence | ~["\\\r\n])*;
+fragment StringFragment: (EscapeSequence | ~["\\\r\n]);
-STRING_LITERAL: '"' StringFragment '"';
+STRING_LITERAL: '"' StringFragment* '"';
+
+STRING_TEMPLATE_BEGIN: '"'
+ { _input.LA(1) != '"' }?
+ { contextCache.enterTemplateContext(StringTemplate); }
+ ;
TEXT_BLOCK_LITERAL_BEGIN: '"' '"' '"' -> pushMode(TextBlock);
@@ -261,8 +271,12 @@ LITERAL_NULL: 'null';
LPAREN: '(';
RPAREN: ')';
-LCURLY: '{';
-RCURLY: '}';
+LCURLY: '{'
+ { contextCache.updateLeftCurlyBraceContext(); }
+ ;
+RCURLY: '}'
+ { contextCache.updateRightCurlyBraceContext(); }
+ ;
LBRACK: '[';
RBRACK: ']';
SEMI: ';';
@@ -321,18 +335,7 @@ AT: '@';
ELLIPSIS: '...';
// String templates
-STRING_TEMPLATE_BEGIN: '"' StringFragment '\\' '{'
- {stringTemplateDepth++;}
- ;
-STRING_TEMPLATE_MID: {stringTemplateDepth > 0}?
- '}' StringFragment '\\' '{'
- ;
-
-STRING_TEMPLATE_END: {stringTemplateDepth > 0}?
- '}' StringFragment '"'
- {stringTemplateDepth--;}
- ;
// Text block fragments
@@ -464,3 +467,11 @@ mode TextBlock;
TEXT_BLOCK_LITERAL_END
: '"' '"' '"' -> popMode
;
+
+mode StringTemplate;
+
+ STRING_TEMPLATE_CONTENT: StringFragment+;
+
+ EMBEDDED_EXPRESSION_BEGIN: '\\' '{' -> pushMode(DEFAULT_MODE);
+
+ STRING_TEMPLATE_END: '"' { contextCache.exitTemplateContext(); };
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..9db56a2dea6 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
@@ -767,14 +767,34 @@ template
: stringTemplate
;
+
stringTemplate
- : STRING_TEMPLATE_BEGIN expr? stringTemplateMiddle* STRING_TEMPLATE_END
+ : stringTemplateBegin expr? stringTemplateMiddle* stringTemplateEnd
;
stringTemplateMiddle
- : STRING_TEMPLATE_MID expr?
+ : stringTemplateMid expr?
+ ;
+
+stringTemplateBegin
+ : STRING_TEMPLATE_BEGIN
+ STRING_TEMPLATE_CONTENT?
+ EMBEDDED_EXPRESSION_BEGIN
+ ;
+
+stringTemplateMid
+ : EMBEDDED_EXPRESSION_END
+ STRING_TEMPLATE_CONTENT?
+ EMBEDDED_EXPRESSION_BEGIN
+ ;
+
+stringTemplateEnd
+ : EMBEDDED_EXPRESSION_END
+ STRING_TEMPLATE_CONTENT?
+ STRING_TEMPLATE_END
;
+
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..3caa27216f6 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/JavaAstVisitorTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/JavaAstVisitorTest.java
@@ -40,11 +40,13 @@
import com.puppycrawl.tools.checkstyle.api.FileContents;
import com.puppycrawl.tools.checkstyle.api.FileText;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+import com.puppycrawl.tools.checkstyle.grammar.CompositeLexerContextCache;
import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer;
import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser;
import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParserBaseVisitor;
import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
+// -@cs[ClassDataAbstractionCoupling] No way to split up class usage.
public class JavaAstVisitorTest extends AbstractModuleTestSupport {
/**
@@ -240,7 +242,9 @@ public void testNoStackOverflowOnDeepStringConcat() throws Exception {
final String fullText = contents.getText().getFullText().toString();
final CharStream codePointCharStream = CharStreams.fromString(fullText);
final JavaLanguageLexer lexer = new JavaLanguageLexer(codePointCharStream, true);
+ final CompositeLexerContextCache contextCache = new CompositeLexerContextCache(lexer);
lexer.setCommentListener(contents);
+ lexer.setContextCache(contextCache);
final CommonTokenStream tokenStream = new CommonTokenStream(lexer);
final JavaLanguageParser parser = new JavaLanguageParser(tokenStream);
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 89ba8c9361d..237a21e192a 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
@@ -46,6 +46,15 @@ public void testBasicStringTemplate() throws Exception {
"InputStringTemplateBasic.java"));
}
+ @Test
+ public void testStringTemplateNested() throws Exception {
+ verifyAst(
+ getNonCompilablePath(
+ "ExpectedStringTemplateNested.txt"),
+ getNonCompilablePath(
+ "InputStringTemplateNested.java"));
+ }
+
/**
* Test for tabs instead of spaces in the input file.
* All node columns are -3 when compared to the above test. This is
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateBasic.txt b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateBasic.txt
index 605e052c881..bda890d21a2 100644
--- a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateBasic.txt
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateBasic.txt
@@ -81,10 +81,8 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | `--DOT -> . [16:26]
| | |--IDENT -> STR [16:23]
| | `--STRING_TEMPLATE_BEGIN -> " [16:27]
- | | |--STRING_TEMPLATE_CONTENT -> [16:28]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [16:28]
| | |--EMBEDDED_EXPRESSION_END -> } [16:32]
- | | |--STRING_TEMPLATE_CONTENT -> [16:33]
| | `--STRING_TEMPLATE_END -> " [16:33]
| `--SEMI -> ; [16:34]
|--VARIABLE_DEF -> VARIABLE_DEF [19:4]
@@ -115,7 +113,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | `--DOT -> . [22:24]
| | |--IDENT -> STR [22:21]
| | `--STRING_TEMPLATE_BEGIN -> " [22:26]
- | | |--STRING_TEMPLATE_CONTENT -> [22:27]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [22:27]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [22:31]
| | | `--METHOD_CALL -> ( [22:31]
@@ -130,7 +127,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | `--RPAREN -> ) [22:37]
| | | `--RPAREN -> ) [22:38]
| | |--EMBEDDED_EXPRESSION_END -> } [22:40]
- | | |--STRING_TEMPLATE_CONTENT -> [22:41]
| | `--STRING_TEMPLATE_END -> " [22:41]
| `--SEMI -> ; [22:42]
|--VARIABLE_DEF -> VARIABLE_DEF [25:4]
@@ -144,22 +140,18 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--DOT -> . [25:24]
| | | |--IDENT -> STR [25:21]
| | | `--STRING_TEMPLATE_BEGIN -> " [25:26]
- | | | |--STRING_TEMPLATE_CONTENT -> [25:27]
| | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [25:27]
| | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [25:30]
| | | | `--IDENT -> x [25:30]
| | | |--EMBEDDED_EXPRESSION_END -> } [25:32]
- | | | |--STRING_TEMPLATE_CONTENT -> [25:33]
| | | `--STRING_TEMPLATE_END -> " [25:33]
| | `--DOT -> . [25:41]
| | |--IDENT -> STR [25:38]
| | `--STRING_TEMPLATE_BEGIN -> " [25:43]
- | | |--STRING_TEMPLATE_CONTENT -> [25:44]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [25:44]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [25:47]
| | | `--IDENT -> y [25:47]
| | |--EMBEDDED_EXPRESSION_END -> } [25:49]
- | | |--STRING_TEMPLATE_CONTENT -> [25:50]
| | `--STRING_TEMPLATE_END -> " [25:50]
| `--SEMI -> ; [25:51]
|--VARIABLE_DEF -> VARIABLE_DEF [28:4]
@@ -173,7 +165,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--DOT -> . [28:24]
| | | |--IDENT -> STR [28:21]
| | | `--STRING_TEMPLATE_BEGIN -> " [28:26]
- | | | |--STRING_TEMPLATE_CONTENT -> [28:27]
| | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [28:27]
| | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [28:31]
| | | | `--METHOD_CALL -> ( [28:31]
@@ -188,12 +179,10 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | | `--RPAREN -> ) [28:37]
| | | | `--RPAREN -> ) [28:38]
| | | |--EMBEDDED_EXPRESSION_END -> } [28:40]
- | | | |--STRING_TEMPLATE_CONTENT -> [28:41]
| | | `--STRING_TEMPLATE_END -> " [28:41]
| | `--DOT -> . [28:49]
| | |--IDENT -> STR [28:46]
| | `--STRING_TEMPLATE_BEGIN -> " [28:51]
- | | |--STRING_TEMPLATE_CONTENT -> [28:52]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [28:52]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [28:56]
| | | `--METHOD_CALL -> ( [28:56]
@@ -208,7 +197,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | `--RPAREN -> ) [28:62]
| | | `--RPAREN -> ) [28:63]
| | |--EMBEDDED_EXPRESSION_END -> } [28:65]
- | | |--STRING_TEMPLATE_CONTENT -> [28:66]
| | `--STRING_TEMPLATE_END -> " [28:66]
| `--SEMI -> ; [28:67]
|--VARIABLE_DEF -> VARIABLE_DEF [31:4]
@@ -222,22 +210,18 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--DOT -> . [31:24]
| | | |--IDENT -> STR [31:21]
| | | `--STRING_TEMPLATE_BEGIN -> " [31:26]
- | | | |--STRING_TEMPLATE_CONTENT -> [31:27]
| | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [31:27]
| | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [31:30]
| | | | `--IDENT -> x [31:30]
| | | |--EMBEDDED_EXPRESSION_END -> } [31:32]
- | | | |--STRING_TEMPLATE_CONTENT -> [31:33]
| | | `--STRING_TEMPLATE_END -> " [31:33]
| | `--DOT -> . [31:41]
| | |--IDENT -> STR [31:38]
| | `--STRING_TEMPLATE_BEGIN -> " [31:43]
- | | |--STRING_TEMPLATE_CONTENT -> [31:44]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [31:44]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [31:47]
| | | `--IDENT -> y [31:47]
| | |--EMBEDDED_EXPRESSION_END -> } [31:49]
- | | |--STRING_TEMPLATE_CONTENT -> [31:50]
| | `--STRING_TEMPLATE_END -> " [31:50]
| `--SEMI -> ; [31:51]
|--VARIABLE_DEF -> VARIABLE_DEF [34:4]
@@ -250,14 +234,12 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | `--DOT -> . [34:24]
| | |--IDENT -> STR [34:21]
| | `--STRING_TEMPLATE_BEGIN -> " [34:26]
- | | |--STRING_TEMPLATE_CONTENT -> [34:27]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [34:27]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [34:32]
| | | `--PLUS -> + [34:32]
| | | |--IDENT -> x [34:30]
| | | `--IDENT -> y [34:34]
| | |--EMBEDDED_EXPRESSION_END -> } [34:36]
- | | |--STRING_TEMPLATE_CONTENT -> [34:37]
| | `--STRING_TEMPLATE_END -> " [34:37]
| `--SEMI -> ; [34:38]
|--VARIABLE_DEF -> VARIABLE_DEF [37:4]
@@ -377,6 +359,8 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | `--RPAREN -> ) [46:31]
| | | `--RPAREN -> ) [46:32]
| | |--EMBEDDED_EXPRESSION_END -> } [47:12]
+ | | |--STRING_TEMPLATE_CONTENT -> there [47:13]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [47:20]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [49:12]
| | | `--PLUS -> + [49:12]
| | | |--PLUS -> + [48:22]
@@ -412,10 +396,7 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | |--EMBEDDED_EXPRESSION_END -> } [49:25]
| | | |--STRING_TEMPLATE_CONTENT -> x [49:26]
| | | `--STRING_TEMPLATE_END -> " [49:27]
- | | |--STRING_TEMPLATE_CONTENT -> there [47:13]
- | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [47:20]
| | |--EMBEDDED_EXPRESSION_END -> } [50:12]
- | | |--STRING_TEMPLATE_CONTENT -> [50:13]
| | `--STRING_TEMPLATE_END -> " [50:13]
| `--SEMI -> ; [50:14]
|--VARIABLE_DEF -> VARIABLE_DEF [52:4]
@@ -522,10 +503,8 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | `--NUM_INT -> 0 [62:17]
| | | `--RPAREN -> ) [62:18]
| | `--STRING_TEMPLATE_BEGIN -> " [62:20]
- | | |--STRING_TEMPLATE_CONTENT -> [62:21]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [62:21]
| | |--EMBEDDED_EXPRESSION_END -> } [62:25]
- | | |--STRING_TEMPLATE_CONTENT -> [62:26]
| | `--STRING_TEMPLATE_END -> " [62:26]
| |--SEMI -> ; [62:27]
| |--EXPR -> EXPR [63:19]
@@ -563,15 +542,15 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:26]
| | | `--IDENT -> x [64:26]
| | |--EMBEDDED_EXPRESSION_END -> } [64:28]
- | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:33]
- | | | `--IDENT -> y [64:33]
| | |--STRING_TEMPLATE_CONTENT -> . [64:29]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [64:30]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:33]
+ | | | `--IDENT -> y [64:33]
| | |--EMBEDDED_EXPRESSION_END -> } [64:35]
- | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:40]
- | | | `--IDENT -> x [64:40]
| | |--STRING_TEMPLATE_CONTENT -> . [64:36]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [64:37]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:40]
+ | | | `--IDENT -> x [64:40]
| | |--EMBEDDED_EXPRESSION_END -> } [64:42]
| | |--STRING_TEMPLATE_CONTENT -> . [64:43]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [64:44]
@@ -579,7 +558,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--STRING_TEMPLATE_CONTENT -> . [64:47]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [64:48]
| | |--EMBEDDED_EXPRESSION_END -> } [64:50]
- | | |--STRING_TEMPLATE_CONTENT -> [64:51]
| | `--STRING_TEMPLATE_END -> " [64:51]
| |--SEMI -> ; [64:52]
| `--RCURLY -> } [65:4]
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateBasicWithTabs.txt b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateBasicWithTabs.txt
index b732010600f..ecbb0b3520b 100644
--- a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateBasicWithTabs.txt
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateBasicWithTabs.txt
@@ -81,10 +81,8 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | `--DOT -> . [16:23]
| | |--IDENT -> STR [16:20]
| | `--STRING_TEMPLATE_BEGIN -> " [16:24]
- | | |--STRING_TEMPLATE_CONTENT -> [16:25]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [16:25]
| | |--EMBEDDED_EXPRESSION_END -> } [16:29]
- | | |--STRING_TEMPLATE_CONTENT -> [16:30]
| | `--STRING_TEMPLATE_END -> " [16:30]
| `--SEMI -> ; [16:31]
|--VARIABLE_DEF -> VARIABLE_DEF [19:1]
@@ -115,7 +113,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | `--DOT -> . [22:21]
| | |--IDENT -> STR [22:18]
| | `--STRING_TEMPLATE_BEGIN -> " [22:23]
- | | |--STRING_TEMPLATE_CONTENT -> [22:24]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [22:24]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [22:28]
| | | `--METHOD_CALL -> ( [22:28]
@@ -130,7 +127,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | `--RPAREN -> ) [22:34]
| | | `--RPAREN -> ) [22:35]
| | |--EMBEDDED_EXPRESSION_END -> } [22:37]
- | | |--STRING_TEMPLATE_CONTENT -> [22:38]
| | `--STRING_TEMPLATE_END -> " [22:38]
| `--SEMI -> ; [22:39]
|--VARIABLE_DEF -> VARIABLE_DEF [25:1]
@@ -144,22 +140,18 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--DOT -> . [25:21]
| | | |--IDENT -> STR [25:18]
| | | `--STRING_TEMPLATE_BEGIN -> " [25:23]
- | | | |--STRING_TEMPLATE_CONTENT -> [25:24]
| | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [25:24]
| | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [25:27]
| | | | `--IDENT -> x [25:27]
| | | |--EMBEDDED_EXPRESSION_END -> } [25:29]
- | | | |--STRING_TEMPLATE_CONTENT -> [25:30]
| | | `--STRING_TEMPLATE_END -> " [25:30]
| | `--DOT -> . [25:38]
| | |--IDENT -> STR [25:35]
| | `--STRING_TEMPLATE_BEGIN -> " [25:40]
- | | |--STRING_TEMPLATE_CONTENT -> [25:41]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [25:41]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [25:44]
| | | `--IDENT -> y [25:44]
| | |--EMBEDDED_EXPRESSION_END -> } [25:46]
- | | |--STRING_TEMPLATE_CONTENT -> [25:47]
| | `--STRING_TEMPLATE_END -> " [25:47]
| `--SEMI -> ; [25:48]
|--VARIABLE_DEF -> VARIABLE_DEF [28:1]
@@ -173,7 +165,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--DOT -> . [28:21]
| | | |--IDENT -> STR [28:18]
| | | `--STRING_TEMPLATE_BEGIN -> " [28:23]
- | | | |--STRING_TEMPLATE_CONTENT -> [28:24]
| | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [28:24]
| | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [28:28]
| | | | `--METHOD_CALL -> ( [28:28]
@@ -188,12 +179,10 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | | `--RPAREN -> ) [28:34]
| | | | `--RPAREN -> ) [28:35]
| | | |--EMBEDDED_EXPRESSION_END -> } [28:37]
- | | | |--STRING_TEMPLATE_CONTENT -> [28:38]
| | | `--STRING_TEMPLATE_END -> " [28:38]
| | `--DOT -> . [28:46]
| | |--IDENT -> STR [28:43]
| | `--STRING_TEMPLATE_BEGIN -> " [28:48]
- | | |--STRING_TEMPLATE_CONTENT -> [28:49]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [28:49]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [28:53]
| | | `--METHOD_CALL -> ( [28:53]
@@ -208,7 +197,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | `--RPAREN -> ) [28:59]
| | | `--RPAREN -> ) [28:60]
| | |--EMBEDDED_EXPRESSION_END -> } [28:62]
- | | |--STRING_TEMPLATE_CONTENT -> [28:63]
| | `--STRING_TEMPLATE_END -> " [28:63]
| `--SEMI -> ; [28:64]
|--VARIABLE_DEF -> VARIABLE_DEF [31:1]
@@ -222,22 +210,18 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--DOT -> . [31:21]
| | | |--IDENT -> STR [31:18]
| | | `--STRING_TEMPLATE_BEGIN -> " [31:23]
- | | | |--STRING_TEMPLATE_CONTENT -> [31:24]
| | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [31:24]
| | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [31:27]
| | | | `--IDENT -> x [31:27]
| | | |--EMBEDDED_EXPRESSION_END -> } [31:29]
- | | | |--STRING_TEMPLATE_CONTENT -> [31:30]
| | | `--STRING_TEMPLATE_END -> " [31:30]
| | `--DOT -> . [31:38]
| | |--IDENT -> STR [31:35]
| | `--STRING_TEMPLATE_BEGIN -> " [31:40]
- | | |--STRING_TEMPLATE_CONTENT -> [31:41]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [31:41]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [31:44]
| | | `--IDENT -> y [31:44]
| | |--EMBEDDED_EXPRESSION_END -> } [31:46]
- | | |--STRING_TEMPLATE_CONTENT -> [31:47]
| | `--STRING_TEMPLATE_END -> " [31:47]
| `--SEMI -> ; [31:48]
|--VARIABLE_DEF -> VARIABLE_DEF [34:1]
@@ -250,14 +234,12 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | `--DOT -> . [34:21]
| | |--IDENT -> STR [34:18]
| | `--STRING_TEMPLATE_BEGIN -> " [34:23]
- | | |--STRING_TEMPLATE_CONTENT -> [34:24]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [34:24]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [34:29]
| | | `--PLUS -> + [34:29]
| | | |--IDENT -> x [34:27]
| | | `--IDENT -> y [34:31]
| | |--EMBEDDED_EXPRESSION_END -> } [34:33]
- | | |--STRING_TEMPLATE_CONTENT -> [34:34]
| | `--STRING_TEMPLATE_END -> " [34:34]
| `--SEMI -> ; [34:35]
|--VARIABLE_DEF -> VARIABLE_DEF [37:1]
@@ -377,6 +359,8 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | `--RPAREN -> ) [46:22]
| | | `--RPAREN -> ) [46:23]
| | |--EMBEDDED_EXPRESSION_END -> } [47:3]
+ | | |--STRING_TEMPLATE_CONTENT -> there [47:4]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [47:11]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [49:3]
| | | `--PLUS -> + [49:3]
| | | |--PLUS -> + [48:13]
@@ -412,10 +396,7 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | |--EMBEDDED_EXPRESSION_END -> } [49:16]
| | | |--STRING_TEMPLATE_CONTENT -> x [49:17]
| | | `--STRING_TEMPLATE_END -> " [49:18]
- | | |--STRING_TEMPLATE_CONTENT -> there [47:4]
- | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [47:11]
| | |--EMBEDDED_EXPRESSION_END -> } [50:3]
- | | |--STRING_TEMPLATE_CONTENT -> [50:4]
| | `--STRING_TEMPLATE_END -> " [50:4]
| `--SEMI -> ; [50:5]
|--VARIABLE_DEF -> VARIABLE_DEF [52:1]
@@ -522,10 +503,8 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | | | `--NUM_INT -> 0 [62:11]
| | | `--RPAREN -> ) [62:12]
| | `--STRING_TEMPLATE_BEGIN -> " [62:14]
- | | |--STRING_TEMPLATE_CONTENT -> [62:15]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [62:15]
| | |--EMBEDDED_EXPRESSION_END -> } [62:19]
- | | |--STRING_TEMPLATE_CONTENT -> [62:20]
| | `--STRING_TEMPLATE_END -> " [62:20]
| |--SEMI -> ; [62:21]
| |--EXPR -> EXPR [63:13]
@@ -563,15 +542,15 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:20]
| | | `--IDENT -> x [64:20]
| | |--EMBEDDED_EXPRESSION_END -> } [64:22]
- | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:27]
- | | | `--IDENT -> y [64:27]
| | |--STRING_TEMPLATE_CONTENT -> . [64:23]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [64:24]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:27]
+ | | | `--IDENT -> y [64:27]
| | |--EMBEDDED_EXPRESSION_END -> } [64:29]
- | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:34]
- | | | `--IDENT -> x [64:34]
| | |--STRING_TEMPLATE_CONTENT -> . [64:30]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [64:31]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [64:34]
+ | | | `--IDENT -> x [64:34]
| | |--EMBEDDED_EXPRESSION_END -> } [64:36]
| | |--STRING_TEMPLATE_CONTENT -> . [64:37]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [64:38]
@@ -579,7 +558,6 @@ COMPILATION_UNIT -> COMPILATION_UNIT [2:0]
| | |--STRING_TEMPLATE_CONTENT -> . [64:41]
| | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [64:42]
| | |--EMBEDDED_EXPRESSION_END -> } [64:44]
- | | |--STRING_TEMPLATE_CONTENT -> [64:45]
| | `--STRING_TEMPLATE_END -> " [64:45]
| |--SEMI -> ; [64:46]
| `--RCURLY -> } [65:1]
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateNested.txt b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateNested.txt
new file mode 100644
index 00000000000..d459f1e28a0
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/ExpectedStringTemplateNested.txt
@@ -0,0 +1,139 @@
+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]
+|--IMPORT -> import [4:0]
+| |--DOT -> . [4:25]
+| | |--DOT -> . [4:16]
+| | | |--DOT -> . [4:11]
+| | | | |--IDENT -> java [4:7]
+| | | | `--IDENT -> util [4:12]
+| | | `--IDENT -> function [4:17]
+| | `--IDENT -> Supplier [4:26]
+| `--SEMI -> ; [4:34]
+`--CLASS_DEF -> CLASS_DEF [6:0]
+ |--MODIFIERS -> MODIFIERS [6:0]
+ | `--LITERAL_PUBLIC -> public [6:0]
+ |--LITERAL_CLASS -> class [6:7]
+ |--IDENT -> InputStringTemplateNested [6:13]
+ `--OBJBLOCK -> OBJBLOCK [6:39]
+ |--LCURLY -> { [6:39]
+ |--VARIABLE_DEF -> VARIABLE_DEF [7:4]
+ | |--MODIFIERS -> MODIFIERS [7:4]
+ | |--TYPE -> TYPE [7:4]
+ | | `--LITERAL_INT -> int [7:4]
+ | |--IDENT -> x [7:8]
+ | |--ASSIGN -> = [7:10]
+ | | `--EXPR -> EXPR [7:12]
+ | | `--NUM_INT -> 2 [7:12]
+ | `--SEMI -> ; [7:13]
+ |--VARIABLE_DEF -> VARIABLE_DEF [8:4]
+ | |--MODIFIERS -> MODIFIERS [8:4]
+ | | `--FINAL -> final [8:4]
+ | |--TYPE -> TYPE [8:10]
+ | | `--IDENT -> String [8:10]
+ | |--IDENT -> s [8:17]
+ | |--ASSIGN -> = [8:19]
+ | | `--EXPR -> EXPR [9:15]
+ | | `--DOT -> . [9:15]
+ | | |--IDENT -> STR [9:12]
+ | | `--STRING_TEMPLATE_BEGIN -> " [9:16]
+ | | |--STRING_TEMPLATE_CONTENT -> x [9:17]
+ | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [9:18]
+ | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [9:23]
+ | | | `--METHOD_CALL -> ( [9:23]
+ | | | |--IDENT -> sp [9:21]
+ | | | |--ELIST -> ELIST [9:27]
+ | | | | `--LAMBDA -> -> [9:27]
+ | | | | |--LPAREN -> ( [9:24]
+ | | | | |--PARAMETERS -> PARAMETERS [9:25]
+ | | | | |--RPAREN -> ) [9:25]
+ | | | | `--SLIST -> { [9:30]
+ | | | | |--LITERAL_RETURN -> return [10:16]
+ | | | | | |--EXPR -> EXPR [10:26]
+ | | | | | | `--DOT -> . [10:26]
+ | | | | | | |--IDENT -> STR [10:23]
+ | | | | | | `--STRING_TEMPLATE_BEGIN -> " [10:27]
+ | | | | | | |--STRING_TEMPLATE_CONTENT -> x [10:28]
+ | | | | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [10:29]
+ | | | | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [10:34]
+ | | | | | | | `--METHOD_CALL -> ( [10:34]
+ | | | | | | | |--IDENT -> sp [10:32]
+ | | | | | | | |--ELIST -> ELIST [10:38]
+ | | | | | | | | `--LAMBDA -> -> [10:38]
+ | | | | | | | | |--LPAREN -> ( [10:35]
+ | | | | | | | | |--PARAMETERS -> PARAMETERS [10:36]
+ | | | | | | | | |--RPAREN -> ) [10:36]
+ | | | | | | | | `--SLIST -> { [10:41]
+ | | | | | | | | |--LITERAL_RETURN -> return [11:20]
+ | | | | | | | | | |--EXPR -> EXPR [11:48]
+ | | | | | | | | | | `--PLUS -> + [11:48]
+ | | | | | | | | | | |--PLUS -> + [11:42]
+ | | | | | | | | | | | |--DOT -> . [11:30]
+ | | | | | | | | | | | | |--IDENT -> STR [11:27]
+ | | | | | | | | | | | | `--STRING_TEMPLATE_BEGIN -> " [11:31]
+ | | | | | | | | | | | | |--STRING_TEMPLATE_CONTENT -> x [11:32]
+ | | | | | | | | | | | | |--EMBEDDED_EXPRESSION_BEGIN -> \{ [11:33]
+ | | | | | | | | | | | | |--EMBEDDED_EXPRESSION -> EMBEDDED_EXPRESSION [11:36]
+ | | | | | | | | | | | | | `--IDENT -> x [11:36]
+ | | | | | | | | | | | | |--EMBEDDED_EXPRESSION_END -> } [11:38]
+ | | | | | | | | | | | | |--STRING_TEMPLATE_CONTENT -> x [11:39]
+ | | | | | | | | | | | | `--STRING_TEMPLATE_END -> " [11:40]
+ | | | | | | | | | | | `--STRING_LITERAL -> "{" [11:44]
+ | | | | | | | | | | `--STRING_LITERAL -> "}}}" [11:50]
+ | | | | | | | | | `--SEMI -> ; [11:55]
+ | | | | | | | | `--RCURLY -> } [12:16]
+ | | | | | | | `--RPAREN -> ) [12:17]
+ | | | | | | |--EMBEDDED_EXPRESSION_END -> } [12:19]
+ | | | | | | |--STRING_TEMPLATE_CONTENT -> x [12:20]
+ | | | | | | `--STRING_TEMPLATE_END -> " [12:21]
+ | | | | | `--SEMI -> ; [12:22]
+ | | | | `--RCURLY -> } [13:12]
+ | | | `--RPAREN -> ) [13:13]
+ | | |--EMBEDDED_EXPRESSION_END -> } [13:14]
+ | | |--STRING_TEMPLATE_CONTENT -> x [13:15]
+ | | `--STRING_TEMPLATE_END -> " [13:16]
+ | `--SEMI -> ; [13:17]
+ |--METHOD_DEF -> METHOD_DEF [15:4]
+ | |--MODIFIERS -> MODIFIERS [15:4]
+ | | |--LITERAL_PRIVATE -> private [15:4]
+ | | `--LITERAL_STATIC -> static [15:12]
+ | |--TYPE -> TYPE [15:19]
+ | | `--IDENT -> String [15:19]
+ | |--IDENT -> sp [15:26]
+ | |--LPAREN -> ( [15:28]
+ | |--PARAMETERS -> PARAMETERS [15:29]
+ | | `--PARAMETER_DEF -> PARAMETER_DEF [15:29]
+ | | |--MODIFIERS -> MODIFIERS [15:29]
+ | | |--TYPE -> TYPE [15:29]
+ | | | |--IDENT -> Supplier [15:29]
+ | | | `--TYPE_ARGUMENTS -> TYPE_ARGUMENTS [15:37]
+ | | | |--GENERIC_START -> < [15:37]
+ | | | |--TYPE_ARGUMENT -> TYPE_ARGUMENT [15:38]
+ | | | | `--IDENT -> String [15:38]
+ | | | `--GENERIC_END -> > [15:44]
+ | | `--IDENT -> y [15:46]
+ | |--RPAREN -> ) [15:47]
+ | `--SLIST -> { [15:49]
+ | |--LITERAL_RETURN -> return [16:8]
+ | | |--EXPR -> EXPR [16:20]
+ | | | `--METHOD_CALL -> ( [16:20]
+ | | | |--DOT -> . [16:16]
+ | | | | |--IDENT -> y [16:15]
+ | | | | `--IDENT -> get [16:17]
+ | | | |--ELIST -> ELIST [16:21]
+ | | | `--RPAREN -> ) [16:21]
+ | | `--SEMI -> ; [16:22]
+ | `--RCURLY -> } [17:4]
+ `--RCURLY -> } [18:0]
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputStringTemplateNested.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputStringTemplateNested.java
new file mode 100644
index 00000000000..7a1bffb8e6d
--- /dev/null
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/grammar/java21/InputStringTemplateNested.java
@@ -0,0 +1,18 @@
+//non-compiled with javac: Compilable with Java21
+package com.puppycrawl.tools.checkstyle.grammar.java21;
+
+import java.util.function.Supplier;
+
+public class InputStringTemplateNested {
+ int x = 2;
+ final String s =
+ STR."x\{ sp(() -> {
+ return STR."x\{ sp(() -> {
+ return STR."x\{ x }x" + "{" + "}}}";
+ }) }x";
+ })}x";
+
+ private static String sp(Supplier y) {
+ return y.get();
+ }
+}