[java] IDEA-365317 integrate parsers

GitOrigin-RevId: 47daae1f9a9f54eed9d0e1485e10959e3447afd2
This commit is contained in:
Mikhail Pyltsin
2025-11-20 12:44:52 +00:00
committed by intellij-monorepo-bot
parent 7831b58155
commit ed8e5dde12
19 changed files with 5676 additions and 210 deletions
@@ -8,6 +8,7 @@ import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ParentAwareTokenSet;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -17,6 +18,14 @@ import static com.intellij.psi.impl.source.BasicJavaDocElementType.BASIC_DOC_COM
import static com.intellij.psi.tree.ParentAwareTokenSet.create;
import static com.intellij.psi.tree.ParentAwareTokenSet.orSet;
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* You can temporarily use @{@link com.intellij.psi.impl.source.OldParserWhiteSpaceAndCommentSetHolder}
*
*/
@ApiStatus.ScheduledForRemoval
@Deprecated
public class WhiteSpaceAndCommentSetHolder {
public static final WhiteSpaceAndCommentSetHolder INSTANCE = new WhiteSpaceAndCommentSetHolder();
@@ -7,7 +7,7 @@ import com.intellij.lang.Language;
import com.intellij.lang.LanguageExtension;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.lang.java.JavaParserDefinition;
import com.intellij.lang.java.parser.BasicJavaParserUtil;
import com.intellij.lang.java.parser.JavaParserUtil;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.platform.backend.workspace.WorkspaceModelTopics;
@@ -73,7 +73,7 @@ public abstract class AbstractBasicJavaParsingTestCase extends ParsingTestCase {
return file;
}
protected void doParserTest(BasicJavaParserUtil.@NotNull ParserWrapper parser) {
protected void doParserTest(JavaParserUtil.@NotNull ParserWrapper parser) {
doParserTest((Object)parser);
}
@@ -138,7 +138,7 @@ public abstract class AbstractBasicJavaParsingTestCase extends ParsingTestCase {
}
protected void doParserTest(@NotNull String text,
BasicJavaParserUtil.@NotNull ParserWrapper parser) {
JavaParserUtil.@NotNull ParserWrapper parser) {
doParserTest(text, (Object)parser);
}
@@ -146,7 +146,7 @@ public abstract class AbstractBasicJavaParsingTestCase extends ParsingTestCase {
@NotNull Object parser) {
String name = getTestName(false);
myFile = myConfigurator.createPsiFile(this, name, text, parser);
if (parser instanceof BasicJavaParserUtil.@NotNull ParserWrapper wrapper) {
if (parser instanceof JavaParserUtil.@NotNull ParserWrapper wrapper) {
myKmpTree = myConfigurator.createFileSyntaxNode(text, wrapper);
}
try {
@@ -1,7 +1,7 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.parser;
import com.intellij.lang.java.parser.BasicJavaParserUtil;
import com.intellij.lang.java.parser.JavaParserUtil;
import com.intellij.platform.syntax.tree.SyntaxNode;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiFile;
@@ -25,7 +25,7 @@ public interface AbstractBasicJavaParsingTestConfigurator {
* @return SyntaxNode node representing the parsed content
*/
@Nullable
SyntaxNode createFileSyntaxNode(@NotNull String text, @Nullable BasicJavaParserUtil.ParserWrapper parserWrapper);
SyntaxNode createFileSyntaxNode(@NotNull String text, @Nullable JavaParserUtil.ParserWrapper parserWrapper);
boolean checkPsi();
@@ -7,7 +7,6 @@ import com.intellij.java.syntax.element.JavaLanguageLevelProvider
import com.intellij.lang.ASTNode
import com.intellij.lang.LanguageASTFactory
import com.intellij.lang.java.JavaLanguage
import com.intellij.lang.java.parser.BasicJavaParserUtil
import com.intellij.lang.java.parser.JavaParserUtil
import com.intellij.lang.java.syntax.JavaElementTypeConverterExtension
import com.intellij.openapi.extensions.ExtensionPointName
@@ -60,7 +59,7 @@ abstract class JavaParsingTestConfiguratorBase(
ourLanguageLevel = languageLevel
}
override fun createFileSyntaxNode(text: String, parserWrapper: BasicJavaParserUtil.ParserWrapper?): SyntaxNode {
override fun createFileSyntaxNode(text: String, parserWrapper: JavaParserUtil.ParserWrapper?): SyntaxNode {
ourLanguageLevel = languageLevel
return parseForSyntaxTree(text) { builder ->
if (parserWrapper != null) {
@@ -78,7 +77,7 @@ abstract class JavaParsingTestConfiguratorBase(
text: String,
parser: Any,
): PsiFile {
ourTestParser = parser as BasicJavaParserUtil.ParserWrapper
ourTestParser = parser as JavaParserUtil.ParserWrapper
val virtualFile = LightVirtualFile("$name.java", JavaFileType.INSTANCE, text, -1)
val psiManager = PsiManager.getInstance(thinJavaParsingTestCase.getProject())
@@ -107,7 +106,7 @@ private fun createBuilder(chameleon: ASTNode?): PsiSyntaxBuilder {
return builder
}
private fun parseWithWrapper(builder: SyntaxTreeBuilder, parser: BasicJavaParserUtil.ParserWrapper) {
private fun parseWithWrapper(builder: SyntaxTreeBuilder, parser: JavaParserUtil.ParserWrapper) {
val root = builder.mark()
parser.parse(builder, ourLanguageLevel)
if (!builder.eof()) {
@@ -121,7 +120,7 @@ private fun parseWithWrapper(builder: SyntaxTreeBuilder, parser: BasicJavaParser
private lateinit var ourLanguageLevel: LanguageLevel
private val ourSyntaxElementType = SyntaxElementType("test.java.file")
private val ourTestFileElementType: IFileElementType = MyIFileElementType()
private var ourTestParser: BasicJavaParserUtil.ParserWrapper? = null
private var ourTestParser: JavaParserUtil.ParserWrapper? = null
private val converter = elementTypeConverterOf(ourSyntaxElementType to ourTestFileElementType)
File diff suppressed because it is too large Load Diff
@@ -1,23 +1,86 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.lang.java.parser;
import com.intellij.core.JavaPsiBundle;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.function.Function;
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public class ExpressionParser extends BasicExpressionParser {
public class ExpressionParser {
static final int FORBID_LAMBDA_MASK = 0x1;
private static final boolean useNewImplementation = Registry.is("pratt.java.expression.parser", true);
private final OldExpressionParser myOldExpressionParser;
private final PrattExpressionParser myNewExpressionParser;
private final JavaParser myParser;
public ExpressionParser(@NotNull JavaParser javaParser) {
super(javaParser, new OldExpressionParser(javaParser), new PrattExpressionParser(javaParser));
@NotNull OldExpressionParser oldExpressionParser = new OldExpressionParser(javaParser);
@NotNull PrattExpressionParser prattExpressionParser = new PrattExpressionParser(javaParser);
this.myOldExpressionParser = oldExpressionParser;
this.myNewExpressionParser = prattExpressionParser;
this.myParser = javaParser;
}
@Override
@Deprecated
public @Nullable PsiBuilder.Marker parseCaseLabel(@NotNull PsiBuilder builder) {
return super.parseCaseLabel(builder);
return myParser.getStatementParser().parseCaseLabel(builder).first;
}
public @Nullable PsiBuilder.Marker parse(@NotNull PsiBuilder builder) {
if (useNewImplementation) {
return myNewExpressionParser.parse(builder);
}
return myOldExpressionParser.parse(builder);
}
PsiBuilder.Marker parseConditionalAndForbiddingLambda(final PsiBuilder builder) {
if (useNewImplementation) {
return myNewExpressionParser.tryParseWithPrecedenceAtMost(builder, PrattExpressionParser.CONDITIONAL_EXPR_PRECEDENCE,
FORBID_LAMBDA_MASK);
}
return myOldExpressionParser.parseConditionalAnd(builder, FORBID_LAMBDA_MASK);
}
public PsiBuilder.Marker parseArgumentList(final PsiBuilder builder) {
if (useNewImplementation) {
return myNewExpressionParser.parseArgumentList(builder);
}
return myOldExpressionParser.parseArgumentList(builder);
}
PsiBuilder.Marker parseArrayInitializer(@NotNull PsiBuilder builder,
@NotNull IElementType type,
@NotNull Function<? super PsiBuilder, PsiBuilder.Marker> elementParser,
@NotNull @PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE) String missingElementKey) {
if (useNewImplementation) {
return myNewExpressionParser.parseArrayInitializer(builder, type, elementParser, missingElementKey);
}
return myOldExpressionParser.parseArrayInitializer(builder, type, elementParser, missingElementKey);
}
PsiBuilder.Marker parseConditional(final PsiBuilder builder) {
if (useNewImplementation) {
return myNewExpressionParser.tryParseWithPrecedenceAtMost(builder, PrattExpressionParser.CONDITIONAL_EXPR_PRECEDENCE,
FORBID_LAMBDA_MASK);
}
return myOldExpressionParser.parseConditional(builder, 0);
}
PsiBuilder.Marker parseAssignmentForbiddingLambda(final PsiBuilder builder) {
if (useNewImplementation) {
return myNewExpressionParser.parse(builder, FORBID_LAMBDA_MASK);
}
return myOldExpressionParser.parseAssignment(builder, FORBID_LAMBDA_MASK);
}
}
@@ -1,16 +1,244 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.java.parser;
import com.intellij.AbstractBundle;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.impl.source.AbstractBasicJavaElementTypeFactory;
import com.intellij.psi.impl.source.BasicElementTypes;
import com.intellij.psi.impl.source.OldParserWhiteSpaceAndCommentSetHolder;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
import static com.intellij.lang.PsiBuilderUtil.expect;
import static com.intellij.lang.java.parser.JavaParserUtil.*;
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public class FileParser extends BasicFileParser {
public class FileParser {
protected final TokenSet IMPORT_LIST_STOPPER_SET;
private final JavaParser myParser;
private final AbstractBasicJavaElementTypeFactory.JavaElementTypeContainer myJavaElementTypeContainer;
private final TokenSet IMPLICIT_CLASS_INDICATORS;
private final OldParserWhiteSpaceAndCommentSetHolder myWhiteSpaceAndCommentSetHolder = OldParserWhiteSpaceAndCommentSetHolder.INSTANCE;
public FileParser(@NotNull JavaParser javaParser) {
super(javaParser);
this.myParser = javaParser;
this.myJavaElementTypeContainer = (javaParser).getJavaElementTypeFactory().getContainer();
this.IMPORT_LIST_STOPPER_SET = TokenSet.orSet(
BasicElementTypes.BASIC_MODIFIER_BIT_SET,
TokenSet.create(JavaTokenType.CLASS_KEYWORD, JavaTokenType.INTERFACE_KEYWORD, JavaTokenType.ENUM_KEYWORD, JavaTokenType.AT));
this.IMPLICIT_CLASS_INDICATORS =
TokenSet.create(this.myJavaElementTypeContainer.METHOD, this.myJavaElementTypeContainer.FIELD,
this.myJavaElementTypeContainer.CLASS_INITIALIZER);
}
public void parse(@NotNull PsiBuilder builder) {
parseFile(builder, this::stopImportListParsing, JavaPsiBundle.INSTANCE, "expected.class.or.interface");
}
public void parseFile(@NotNull PsiBuilder builder,
@NotNull Predicate<? super PsiBuilder> importListStopper,
@NotNull AbstractBundle bundle,
@NotNull String errorMessageKey) {
parsePackageStatement(builder);
Pair<PsiBuilder.Marker, Boolean> impListInfo = parseImportList(builder, importListStopper); // (importList, isEmpty)
Boolean firstDeclarationOk = null;
PsiBuilder.Marker firstDeclaration = null;
PsiBuilder.Marker invalidElements = null;
boolean isImplicitClass = false;
while (!builder.eof()) {
if (builder.getTokenType() == JavaTokenType.SEMICOLON) {
builder.advanceLexer();
continue;
}
PsiBuilder.Marker declaration = myParser.getModuleParser().parse(builder);
if (declaration == null) declaration = parseInitial(builder);
if (declaration != null) {
if (invalidElements != null) {
invalidElements.errorBefore(error(bundle, errorMessageKey), declaration);
invalidElements = null;
}
if (firstDeclarationOk == null) {
firstDeclarationOk = exprType(declaration) != myJavaElementTypeContainer.MODIFIER_LIST;
}
if (firstDeclaration == null) {
firstDeclaration = declaration;
}
if (!isImplicitClass && IMPLICIT_CLASS_INDICATORS.contains(exprType(declaration))) {
isImplicitClass = true;
}
continue;
}
if (invalidElements == null) {
invalidElements = builder.mark();
}
builder.advanceLexer();
if (firstDeclarationOk == null) firstDeclarationOk = false;
}
if (invalidElements != null) {
invalidElements.error(error(bundle, errorMessageKey));
}
if (impListInfo.second && firstDeclarationOk == Boolean.TRUE) {
impListInfo.first.setCustomEdgeTokenBinders(myWhiteSpaceAndCommentSetHolder.getPrecedingCommentBinder(getLanguageLevel(builder)),
null); // pass comments behind fake import list
firstDeclaration.setCustomEdgeTokenBinders(
myWhiteSpaceAndCommentSetHolder.getSpecialPrecedingCommentBinder(getLanguageLevel(builder)), null);
}
if (isImplicitClass) {
PsiBuilder.Marker beforeFirst = firstDeclaration.precede();
done(beforeFirst, myJavaElementTypeContainer.IMPLICIT_CLASS, builder, myWhiteSpaceAndCommentSetHolder);
}
}
private boolean stopImportListParsing(PsiBuilder b) {
IElementType type = b.getTokenType();
if (IMPORT_LIST_STOPPER_SET.contains(type) || DeclarationParser.isRecordToken(b, type)) return true;
if (type == JavaTokenType.IDENTIFIER) {
String text = b.getTokenText();
if (JavaKeywords.OPEN.equals(text) || JavaKeywords.MODULE.equals(text)) return true;
}
return false;
}
protected @Nullable PsiBuilder.Marker parseInitial(PsiBuilder builder) {
return myParser.getDeclarationParser().parse(builder, DeclarationParser.Context.FILE);
}
private void parsePackageStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
if (!expect(builder, JavaTokenType.PACKAGE_KEYWORD)) {
PsiBuilder.Marker modList = builder.mark();
myParser.getDeclarationParser().parseAnnotations(builder);
done(modList, myJavaElementTypeContainer.MODIFIER_LIST, builder, myWhiteSpaceAndCommentSetHolder);
if (!expect(builder, JavaTokenType.PACKAGE_KEYWORD)) {
statement.rollbackTo();
return;
}
}
PsiBuilder.Marker ref = myParser.getReferenceParser().parseJavaCodeReference(builder, true, false, false, false);
if (ref == null) {
statement.error(JavaPsiBundle.message("expected.class.or.interface"));
return;
}
semicolon(builder);
done(statement, myJavaElementTypeContainer.PACKAGE_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
}
protected @NotNull Pair<PsiBuilder.Marker, Boolean> parseImportList(PsiBuilder builder, Predicate<? super PsiBuilder> stopper) {
PsiBuilder.Marker list = builder.mark();
boolean isEmpty = true;
PsiBuilder.Marker invalidElements = null;
while (!builder.eof()) {
if (stopper.test(builder)) {
break;
}
else if (builder.getTokenType() == JavaTokenType.SEMICOLON) {
builder.advanceLexer();
continue;
}
PsiBuilder.Marker statement = parseImportStatement(builder);
if (statement != null) {
isEmpty = false;
if (invalidElements != null) {
invalidElements.errorBefore(JavaPsiBundle.message("unexpected.token"), statement);
invalidElements = null;
}
continue;
}
if (invalidElements == null) {
invalidElements = builder.mark();
}
builder.advanceLexer();
}
if (invalidElements != null) {
invalidElements.rollbackTo();
}
if (isEmpty) {
PsiBuilder.Marker precede = list.precede();
list.rollbackTo();
list = precede;
}
done(list, myJavaElementTypeContainer.IMPORT_LIST, builder, myWhiteSpaceAndCommentSetHolder);
return Pair.create(list, isEmpty);
}
protected @Nullable PsiBuilder.Marker parseImportStatement(PsiBuilder builder) {
if (builder.getTokenType() != JavaTokenType.IMPORT_KEYWORD) return null;
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
String identifierText = builder.getTokenText();
IElementType type = getImportType(builder);
boolean isStatic = type == myJavaElementTypeContainer.IMPORT_STATIC_STATEMENT;
boolean isModule = type == myJavaElementTypeContainer.IMPORT_MODULE_STATEMENT;
final boolean isOk;
if (isModule) {
isOk = myParser.getModuleParser().parseName(builder) != null;
}
else {
isOk = myParser.getReferenceParser().parseImportCodeReference(builder, isStatic);
}
//if it is `module` we should expect either `;` or `identifier`
if (isOk && !isModule && !isStatic && builder.getTokenType() != JavaTokenType.SEMICOLON &&
JavaKeywords.MODULE.equals(identifierText)) {
JavaParserUtil.error(builder, JavaPsiBundle.message("expected.identifier.or.semicolon"));
}
else if (isOk) {
semicolon(builder);
}
done(statement, type, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull IElementType getImportType(@NotNull PsiBuilder builder) {
IElementType type = builder.getTokenType();
if (type == JavaTokenType.STATIC_KEYWORD) {
builder.advanceLexer();
return myJavaElementTypeContainer.IMPORT_STATIC_STATEMENT;
}
if (type == JavaTokenType.IDENTIFIER &&
JavaKeywords.MODULE.equals(builder.getTokenText()) &&
builder.lookAhead(1) == JavaTokenType.IDENTIFIER) {
builder.remapCurrentToken(JavaTokenType.MODULE_KEYWORD);
builder.advanceLexer();
return myJavaElementTypeContainer.IMPORT_MODULE_STATEMENT;
}
return myJavaElementTypeContainer.IMPORT_STATEMENT;
}
private static @NotNull @NlsContexts.ParsingError String error(@NotNull AbstractBundle bundle, @NotNull String errorMessageKey) {
return bundle.getMessage(errorMessageKey);
}
}
@@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
* @deprecated Use {@link com.intellij.java.syntax.parser.JavaParser} instead
*/
@Deprecated
public class JavaParser extends BasicJavaParser {
public class JavaParser {
public static final JavaParser INSTANCE = new JavaParser();
private final FileParser myFileParser;
@@ -30,42 +30,34 @@ public class JavaParser extends BasicJavaParser {
myPatternParser = new PatternParser(this);
}
@Override
public @NotNull FileParser getFileParser() {
return myFileParser;
}
@Override
public @NotNull ModuleParser getModuleParser() {
return myModuleParser;
}
@Override
public @NotNull DeclarationParser getDeclarationParser() {
return myDeclarationParser;
}
@Override
public @NotNull StatementParser getStatementParser() {
return myStatementParser;
}
@Override
public @NotNull ExpressionParser getExpressionParser() {
return myExpressionParser;
}
@Override
public @NotNull ReferenceParser getReferenceParser() {
return myReferenceParser;
}
@Override
public @NotNull PatternParser getPatternParser() {
return myPatternParser;
}
@Override
public AbstractBasicJavaElementTypeFactory getJavaElementTypeFactory() {
return JavaElementTypeFactory.INSTANCE;
}
@@ -3,37 +3,51 @@ package com.intellij.lang.java.parser;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.syntax.element.SyntaxElementTypes;
import com.intellij.java.syntax.element.lazyParser.ParsingUtil;
import com.intellij.java.syntax.lexer.JavaLexer;
import com.intellij.lang.ASTNode;
import com.intellij.lang.LighterLazyParseableNode;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.WhitespacesAndCommentsBinder;
import com.intellij.lang.*;
import com.intellij.lang.impl.PsiBuilderAdapter;
import com.intellij.lang.impl.TokenSequence;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.lang.java.JavaParserDefinition;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Pair;
import com.intellij.platform.syntax.SyntaxElementType;
import com.intellij.platform.syntax.SyntaxElementTypeSet;
import com.intellij.platform.syntax.element.SyntaxTokenTypes;
import com.intellij.platform.syntax.lexer.Lexer;
import com.intellij.platform.syntax.lexer.TokenList;
import com.intellij.platform.syntax.lexer.TokenListUtil;
import com.intellij.platform.syntax.psi.IntelliJLogger;
import com.intellij.platform.syntax.parser.SyntaxTreeBuilder;
import com.intellij.platform.syntax.psi.*;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.OldParserWhiteSpaceAndCommentSetHolder;
import com.intellij.psi.impl.source.WhiteSpaceAndCommentSetHolder;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.indexing.IndexingDataKeys;
import kotlin.Unit;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.function.Function;
import java.util.function.Predicate;
import static com.intellij.platform.syntax.lexer.TokenListUtil.tokenListLexer;
public final class JavaParserUtil {
public static final SyntaxElementTypeSet WS_COMMENTS =
SyntaxElementTypes.INSTANCE.getJAVA_COMMENT_BIT_SET().plus(SyntaxTokenTypes.getWHITE_SPACE());
@@ -41,6 +55,9 @@ public final class JavaParserUtil {
private static final com.intellij.platform.syntax.Logger SYNTAX_LOGGER =
IntelliJLogger.asSyntaxLogger(Logger.getInstance(JavaParserUtil.class));
private static final Key<LanguageLevel> LANG_LEVEL_KEY = Key.create("JavaParserUtil.LanguageLevel");
private static final Key<Boolean> DEEP_PARSE_BLOCKS_IN_STATEMENTS = Key.create("JavaParserUtil.ParserExtender");
public static @NotNull TokenList obtainTokens(@NotNull PsiFile file) {
return CachedValuesManager.getCachedValue(file, () ->
CachedValueProvider.Result.create(
@@ -64,7 +81,8 @@ public final class JavaParserUtil {
}
@FunctionalInterface
public interface ParserWrapper extends BasicJavaParserUtil.ParserWrapper {
public interface ParserWrapper {
void parse(@NotNull SyntaxTreeBuilder builder, @NotNull LanguageLevel languageLevel);
}
/**
@@ -94,183 +112,417 @@ public final class JavaParserUtil {
private JavaParserUtil() { }
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void setLanguageLevel(final PsiBuilder builder, final LanguageLevel level) {
BasicJavaParserUtil.setLanguageLevel(builder, level);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static @NotNull LanguageLevel getLanguageLevel(final PsiBuilder builder) {
return BasicJavaParserUtil.getLanguageLevel(builder);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void setParseStatementCodeBlocksDeep(final PsiBuilder builder, final boolean deep) {
BasicJavaParserUtil.setParseStatementCodeBlocksDeep(builder, deep);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static boolean isParseStatementCodeBlocksDeep(final PsiBuilder builder) {
return BasicJavaParserUtil.isParseStatementCodeBlocksDeep(builder);
}
public static @NotNull PsiSyntaxBuilderWithLanguageLevel createSyntaxBuilder(final ASTNode chameleon) {
return BasicJavaParserUtil.createSyntaxBuilder(chameleon,
(psi) -> PsiUtil.getLanguageLevel(psi),
(psi) -> obtainTokens(psi));
return createSyntaxBuilder(chameleon,
(psi) -> PsiUtil.getLanguageLevel(psi),
(psi) -> obtainTokens(psi));
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static @NotNull PsiBuilder createBuilder(final ASTNode chameleon) {
return BasicJavaParserUtil.createBuilder(chameleon,
(psi) -> PsiUtil.getLanguageLevel(psi),
(level) -> JavaParserDefinition.createLexer(level),
(psi) -> obtainTokensOutdated(psi));
return createBuilder(chameleon,
(psi) -> PsiUtil.getLanguageLevel(psi),
(level) -> JavaParserDefinition.createLexer(level),
(psi) -> obtainTokensOutdated(psi));
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static @NotNull PsiBuilder createBuilder(final LighterLazyParseableNode chameleon) {
return BasicJavaParserUtil.createBuilder(chameleon,
return createBuilder(chameleon,
(psi) -> PsiUtil.getLanguageLevel(psi),
(level) -> JavaParserDefinition.createLexer(level));
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void done(final PsiBuilder.Marker marker, final IElementType type) {
BasicJavaParserUtil.done(marker, type, WhiteSpaceAndCommentSetHolder.INSTANCE);
done(marker, type, WhiteSpaceAndCommentSetHolder.INSTANCE);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void setLanguageLevel(final PsiBuilder builder, final LanguageLevel level) {
builder.putUserData(LANG_LEVEL_KEY, level);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static @NotNull LanguageLevel getLanguageLevel(final PsiBuilder builder) {
final LanguageLevel level = builder.getUserData(LANG_LEVEL_KEY);
assert level != null : builder;
return level;
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void setParseStatementCodeBlocksDeep(final PsiBuilder builder, final boolean deep) {
builder.putUserData(DEEP_PARSE_BLOCKS_IN_STATEMENTS, deep);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static boolean isParseStatementCodeBlocksDeep(final PsiBuilder builder) {
return Boolean.TRUE.equals(builder.getUserData(DEEP_PARSE_BLOCKS_IN_STATEMENTS));
}
/**
* @deprecated use {@link JavaParserUtil#done(PsiBuilder.Marker, IElementType, PsiBuilder, WhiteSpaceAndCommentSetHolder)}
*/
@Deprecated
public static void done(final @NotNull PsiBuilder.Marker marker,
final @NotNull IElementType type,
final @NotNull WhiteSpaceAndCommentSetHolder commentSetHolder) {
marker.done(type);
final WhitespacesAndCommentsBinder left =
commentSetHolder.getPrecedingCommentSet().contains(type) ? commentSetHolder.getPrecedingCommentBinder(LanguageLevel.HIGHEST)
: null;
final WhitespacesAndCommentsBinder right =
commentSetHolder.getTrailingCommentSet().contains(type) ? commentSetHolder.getTrailingCommentBinder()
: null;
marker.setCustomEdgeTokenBinders(left, right);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void done(final @NotNull PsiBuilder.Marker marker,
final @NotNull IElementType type,
final @NotNull PsiBuilder builder,
final @NotNull OldParserWhiteSpaceAndCommentSetHolder commentSetHolder) {
marker.done(type);
final WhitespacesAndCommentsBinder left =
commentSetHolder.getPrecedingCommentSet().contains(type) ? commentSetHolder.getPrecedingCommentBinder(getLanguageLevel(builder))
: null;
final WhitespacesAndCommentsBinder right =
commentSetHolder.getTrailingCommentSet().contains(type) ? commentSetHolder.getTrailingCommentBinder()
: null;
marker.setCustomEdgeTokenBinders(left, right);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static @Nullable IElementType exprType(final @Nullable PsiBuilder.Marker marker) {
return BasicJavaParserUtil.exprType(marker);
return marker != null ? marker.getTokenType() : null;
}
// used instead of PsiBuilder.error() as it keeps all subsequent error messages
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void error(final PsiBuilder builder, @NotNull @NlsContexts.ParsingError String message) {
BasicJavaParserUtil.error(builder, message);
builder.mark().error(message);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void error(final PsiBuilder builder,
@NotNull @NlsContexts.ParsingError String message,
final @Nullable PsiBuilder.Marker before) {
BasicJavaParserUtil.error(builder, message, before);
if (before == null) {
error(builder, message);
}
else {
before.precede().errorBefore(message, before);
}
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static boolean expectOrError(PsiBuilder builder,
TokenSet expected,
@PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE) String key) {
return BasicJavaParserUtil.expectOrError(builder, expected, key);
if (!PsiBuilderUtil.expect(builder, expected)) {
error(builder, JavaPsiBundle.message(key));
return false;
}
return true;
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static boolean expectOrError(PsiBuilder builder,
IElementType expected,
@PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE) String key) {
return BasicJavaParserUtil.expectOrError(builder, expected, key);
if (!PsiBuilderUtil.expect(builder, expected)) {
error(builder, JavaPsiBundle.message(key));
return false;
}
return true;
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void emptyElement(final PsiBuilder builder, final IElementType type) {
BasicJavaParserUtil.emptyElement(builder, type);
builder.mark().done(type);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void emptyElement(final PsiBuilder.Marker before, final IElementType type) {
BasicJavaParserUtil.emptyElement(before, type);
before.precede().doneBefore(type, before);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static void semicolon(final PsiBuilder builder) {
BasicJavaParserUtil.semicolon(builder);
expectOrError(builder, JavaTokenType.SEMICOLON, "expected.semicolon");
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static PsiBuilder braceMatchingBuilder(final PsiBuilder builder) {
return BasicJavaParserUtil.braceMatchingBuilder(builder);
final PsiBuilder.Marker pos = builder.mark();
int braceCount = 1;
while (!builder.eof()) {
final IElementType tokenType = builder.getTokenType();
if (tokenType == JavaTokenType.LBRACE) {
braceCount++;
}
else if (tokenType == JavaTokenType.RBRACE) braceCount--;
if (braceCount == 0) break;
builder.advanceLexer();
}
final int stopAt = builder.getCurrentOffset();
pos.rollbackTo();
return stoppingBuilder(builder, stopAt);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static PsiBuilder stoppingBuilder(final PsiBuilder builder, final int stopAt) {
return BasicJavaParserUtil.stoppingBuilder(builder, stopAt);
return new PsiBuilderAdapter(builder) {
@Override
public IElementType getTokenType() {
return getCurrentOffset() < stopAt ? super.getTokenType() : null;
}
@Override
public boolean eof() {
return getCurrentOffset() >= stopAt || super.eof();
}
};
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static PsiBuilder stoppingBuilder(final PsiBuilder builder, final Predicate<? super Pair<IElementType, String>> condition) {
return BasicJavaParserUtil.stoppingBuilder(builder, condition);
return new PsiBuilderAdapter(builder) {
@Override
public IElementType getTokenType() {
final Pair<IElementType, String> input = Pair.create(builder.getTokenType(), builder.getTokenText());
return condition.test(input) ? null : super.getTokenType();
}
@Override
public boolean eof() {
final Pair<IElementType, String> input = Pair.create(builder.getTokenType(), builder.getTokenText());
return condition.test(input) || super.eof();
}
};
}
public static @Nullable ASTNode parseFragmentWithHighestLanguageLevel(
@NotNull ASTNode chameleon,
@NotNull JavaParserUtil.ParserWrapper wrapper,
@NotNull Function<LanguageLevel, Lexer> lexer
) {
return parseFragment(chameleon, wrapper, true, LanguageLevel.HIGHEST, lexer);
}
public static @Nullable ASTNode parseFragment(
@NotNull ASTNode chameleon,
@NotNull JavaParserUtil.ParserWrapper wrapper,
boolean eatAll,
@NotNull LanguageLevel level) {
return parseFragment(chameleon, wrapper, eatAll, level, JavaLexer::new);
}
public static @Nullable ASTNode parseFragment(
@NotNull ASTNode chameleon,
@NotNull JavaParserUtil.ParserWrapper wrapper,
boolean eatAll,
@NotNull LanguageLevel level,
@NotNull Function<LanguageLevel, ? extends Lexer> javaLexer
) {
final PsiElement psi = chameleon.getTreeParent() != null ? chameleon.getTreeParent().getPsi() : chameleon.getPsi();
assert psi != null : chameleon;
PsiSyntaxBuilderFactory factory = PsiSyntaxBuilderFactory.getInstance();
Lexer lexer = javaLexer.apply(level);
PsiSyntaxBuilder psiBuilder = factory.createBuilder(chameleon, lexer, chameleon.getElementType().getLanguage(), chameleon.getChars());
long startTime = System.nanoTime();
SyntaxTreeBuilder builder = psiBuilder.getSyntaxTreeBuilder();
ElementTypeConverter converter = ElementTypeConverters.getConverter(JavaLanguage.INSTANCE);
SyntaxElementType type = ElementTypeConverterKt.convertNotNull(converter, chameleon.getElementType());
ParsingUtil.parseFragment(builder, type, eatAll, () -> {
wrapper.parse(builder, level);
return Unit.INSTANCE;
});
ASTNode result = psiBuilder.getTreeBuilt().getFirstChildNode();
ParsingDiagnostics.registerParse(builder, chameleon.getElementType().getLanguage(), System.nanoTime() - startTime);
return result;
}
public static @NotNull PsiSyntaxBuilderWithLanguageLevel createSyntaxBuilder(@NotNull ASTNode chameleon,
@NotNull Function<PsiElement, LanguageLevel> languageLevelFunction,
@NotNull Function<PsiFile, TokenList> psiAsLexer) {
PsiElement psi = chameleon.getPsi();
assert psi != null : chameleon;
CharSequence indexedText = psi.getUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY);
CharSequence text;
if (TreeUtil.isCollapsedChameleon(chameleon)) {
text = chameleon.getChars();
}
else {
text = indexedText;
if (text == null) text = chameleon.getChars();
}
LanguageLevel level = languageLevelFunction.apply(psi);
Lexer lexer = psi instanceof PsiFile && indexedText != null ? tokenListLexer(psiAsLexer.apply((PsiFile)psi), SYNTAX_LOGGER)
: new JavaLexer(level);
Language language = psi.getLanguage();
if (!language.isKindOf(JavaLanguage.INSTANCE)) language = JavaLanguage.INSTANCE;
PsiSyntaxBuilderFactory factory = PsiSyntaxBuilderFactory.getInstance();
PsiSyntaxBuilder builder = factory.createBuilder(chameleon, lexer, language, text);
return new PsiSyntaxBuilderWithLanguageLevel(builder, level);
}
public static @NotNull PsiSyntaxBuilderWithLanguageLevel createSyntaxBuilder(@NotNull LighterLazyParseableNode chameleon,
@NotNull Function<PsiElement, LanguageLevel> languageLevelFunction) {
PsiElement psi = chameleon.getContainingFile();
assert psi != null : chameleon;
PsiSyntaxBuilderFactory factory = PsiSyntaxBuilderFactory.getInstance();
LanguageLevel level = languageLevelFunction.apply(psi);
Lexer lexer = new JavaLexer(level);
PsiSyntaxBuilder builder = factory.createBuilder(chameleon, lexer, chameleon.getTokenType().getLanguage(), chameleon.getText());
return new PsiSyntaxBuilderWithLanguageLevel(builder, level);
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static @NotNull PsiBuilder createBuilder(final @NotNull ASTNode chameleon,
@NotNull Function<PsiElement, LanguageLevel> languageLevelFunction,
@NotNull Function<LanguageLevel, com.intellij.lexer.Lexer> lexerFunction,
@NotNull Function<PsiFile, com.intellij.lexer.TokenList> psiAsLexer) {
final PsiElement psi = chameleon.getPsi();
assert psi != null : chameleon;
final Project project = psi.getProject();
CharSequence indexedText = psi.getUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY);
CharSequence text;
if (TreeUtil.isCollapsedChameleon(chameleon)) {
text = chameleon.getChars();
}
else {
text = indexedText;
if (text == null) text = chameleon.getChars();
}
LanguageLevel level = languageLevelFunction.apply(psi);
com.intellij.lexer.Lexer lexer = psi instanceof PsiFile && indexedText != null ? psiAsLexer.apply((PsiFile)psi).asLexer()
: lexerFunction.apply(level);
Language language = psi.getLanguage();
if (!language.isKindOf(JavaLanguage.INSTANCE)) language = JavaLanguage.INSTANCE;
PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(project, chameleon, lexer, language, text);
setLanguageLevel(builder, level);
return builder;
}
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public static @NotNull PsiBuilder createBuilder(@NotNull LighterLazyParseableNode chameleon,
@NotNull Function<PsiElement, LanguageLevel> languageLevelFunction,
@NotNull Function<LanguageLevel, com.intellij.lexer.Lexer> lexerFunction) {
final PsiElement psi = chameleon.getContainingFile();
assert psi != null : chameleon;
final Project project = psi.getProject();
final PsiBuilderFactory factory = PsiBuilderFactory.getInstance();
final LanguageLevel level = languageLevelFunction.apply(psi);
final com.intellij.lexer.Lexer lexer = lexerFunction.apply(level);
final PsiBuilder builder =
factory.createBuilder(project, chameleon, lexer, chameleon.getTokenType().getLanguage(), chameleon.getText());
setLanguageLevel(builder, level);
return builder;
}
}
@@ -1,30 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.lang.java.parser;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.impl.source.tree.JavaDocElementTypeFactory;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@ApiStatus.ScheduledForRemoval
@Deprecated
public final class JavadocParser {
private JavadocParser() { }
public static void parseJavadocReference(@NotNull PsiBuilder builder) {
BasicJavaDocParser.parseJavadocReference(builder, JavaParser.INSTANCE);
}
public static void parseJavadocType(@NotNull PsiBuilder builder) {
BasicJavaDocParser.parseJavadocType(builder, JavaParser.INSTANCE);
}
public static void parseDocCommentText(@NotNull PsiBuilder builder) {
BasicJavaDocParser.parseDocCommentText(builder, JavaDocElementTypeFactory.INSTANCE.getContainer());
}
}
@@ -15,15 +15,328 @@
*/
package com.intellij.lang.java.parser;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.PsiBuilder;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.impl.source.AbstractBasicJavaElementTypeFactory;
import com.intellij.psi.impl.source.BasicElementTypes;
import com.intellij.psi.impl.source.OldParserWhiteSpaceAndCommentSetHolder;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import static com.intellij.lang.PsiBuilderUtil.expect;
import static com.intellij.lang.java.parser.JavaParserUtil.error;
import static com.intellij.lang.java.parser.JavaParserUtil.semicolon;
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public class ModuleParser extends BasicModuleParser {
public class ModuleParser {
private static final Set<String> STATEMENT_KEYWORDS =
ContainerUtil.newHashSet(JavaKeywords.REQUIRES, JavaKeywords.EXPORTS, JavaKeywords.USES, JavaKeywords.PROVIDES);
private final JavaParser myParser;
private final AbstractBasicJavaElementTypeFactory.JavaElementTypeContainer myJavaElementTypeContainer;
private final OldParserWhiteSpaceAndCommentSetHolder myWhiteSpaceAndCommentSetHolder = OldParserWhiteSpaceAndCommentSetHolder.INSTANCE;
public ModuleParser(@NotNull JavaParser parser) {
super(parser);
this.myParser = parser;
this.myJavaElementTypeContainer = parser.getJavaElementTypeFactory().getContainer();
}
public @Nullable PsiBuilder.Marker parse(@NotNull PsiBuilder builder) {
PsiBuilder.Marker module = builder.mark();
PsiBuilder.Marker firstAnnotation = myParser.getDeclarationParser().parseAnnotations(builder);
IElementType type = builder.getTokenType();
String text = type == JavaTokenType.IDENTIFIER ? builder.getTokenText() : null;
if (!(JavaKeywords.OPEN.equals(text) || JavaKeywords.MODULE.equals(text))) {
module.rollbackTo();
return null;
}
PsiBuilder.Marker modifierList = firstAnnotation != null ? firstAnnotation.precede() : builder.mark();
if (JavaKeywords.OPEN.equals(text)) {
mapAndAdvance(builder, JavaTokenType.OPEN_KEYWORD);
text = builder.getTokenText();
}
JavaParserUtil.done(modifierList, myJavaElementTypeContainer.MODIFIER_LIST, builder, myWhiteSpaceAndCommentSetHolder);
if (JavaKeywords.MODULE.equals(text)) {
mapAndAdvance(builder, JavaTokenType.MODULE_KEYWORD);
}
else {
module.drop();
parseExtras(builder, JavaPsiBundle.message("expected.module.declaration"));
return module;
}
if (parseName(builder) == null) {
module.drop();
if (builder.getTokenType() != null) {
parseExtras(builder, JavaPsiBundle.message("expected.module.declaration"));
}
else {
error(builder, JavaPsiBundle.message("expected.identifier"));
}
return module;
}
if (!expect(builder, JavaTokenType.LBRACE)) {
if (builder.getTokenType() != null) {
parseExtras(builder, JavaPsiBundle.message("expected.module.declaration"));
}
else {
error(builder, JavaPsiBundle.message("expected.lbrace"));
}
}
else {
parseModuleContent(builder);
}
JavaParserUtil.done(module, myJavaElementTypeContainer.MODULE, builder, myWhiteSpaceAndCommentSetHolder);
if (builder.getTokenType() != null) {
parseExtras(builder, JavaPsiBundle.message("unexpected.tokens"));
}
return module;
}
public PsiBuilder.Marker parseName(PsiBuilder builder) {
PsiBuilder.Marker nameElement = builder.mark();
boolean empty = true;
boolean idExpected = true;
while (true) {
IElementType t = builder.getTokenType();
if (t == JavaTokenType.IDENTIFIER) {
if (!idExpected) error(builder, JavaPsiBundle.message("expected.dot"));
idExpected = false;
}
else if (t == JavaTokenType.DOT) {
if (idExpected) error(builder, JavaPsiBundle.message("expected.identifier"));
idExpected = true;
}
else {
break;
}
builder.advanceLexer();
empty = false;
}
if (!empty) {
if (idExpected) error(builder, JavaPsiBundle.message("expected.identifier"));
nameElement.done(myJavaElementTypeContainer.MODULE_REFERENCE);
return nameElement;
}
else {
nameElement.drop();
return null;
}
}
private void parseModuleContent(PsiBuilder builder) {
IElementType token;
PsiBuilder.Marker invalid = null;
while ((token = builder.getTokenType()) != null) {
if (token == JavaTokenType.RBRACE) {
break;
}
if (token == JavaTokenType.SEMICOLON) {
if (invalid != null) {
invalid.error(JavaPsiBundle.message("expected.module.statement"));
invalid = null;
}
builder.advanceLexer();
continue;
}
PsiBuilder.Marker statement = parseStatement(builder);
if (statement == null) {
if (invalid == null) invalid = builder.mark();
builder.advanceLexer();
}
else if (invalid != null) {
invalid.errorBefore(JavaPsiBundle.message("expected.module.statement"), statement);
invalid = null;
}
}
if (invalid != null) {
invalid.error(JavaPsiBundle.message("expected.module.statement"));
}
if (!expect(builder, JavaTokenType.RBRACE) && invalid == null) {
error(builder, JavaPsiBundle.message("expected.rbrace"));
}
}
private PsiBuilder.Marker parseStatement(PsiBuilder builder) {
String kw = builder.getTokenText();
if (JavaKeywords.REQUIRES.equals(kw)) return parseRequiresStatement(builder);
if (JavaKeywords.EXPORTS.equals(kw)) return parseExportsStatement(builder);
if (JavaKeywords.OPENS.equals(kw)) return parseOpensStatement(builder);
if (JavaKeywords.USES.equals(kw)) return parseUsesStatement(builder);
if (JavaKeywords.PROVIDES.equals(kw)) return parseProvidesStatement(builder);
return null;
}
private PsiBuilder.Marker parseRequiresStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
mapAndAdvance(builder, JavaTokenType.REQUIRES_KEYWORD);
PsiBuilder.Marker modifierList = builder.mark();
while (true) {
if (expect(builder, BasicElementTypes.BASIC_MODIFIER_BIT_SET)) continue;
if (builder.getTokenType() == JavaTokenType.IDENTIFIER && JavaKeywords.TRANSITIVE.equals(builder.getTokenText())) {
mapAndAdvance(builder, JavaTokenType.TRANSITIVE_KEYWORD);
continue;
}
break;
}
JavaParserUtil.done(modifierList, myJavaElementTypeContainer.MODIFIER_LIST, builder, myWhiteSpaceAndCommentSetHolder);
if (parseNameRef(builder) != null) {
semicolon(builder);
}
else {
expect(builder, JavaTokenType.SEMICOLON);
}
statement.done(myJavaElementTypeContainer.REQUIRES_STATEMENT);
return statement;
}
private PsiBuilder.Marker parseExportsStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
mapAndAdvance(builder, JavaTokenType.EXPORTS_KEYWORD);
return parsePackageStatement(builder, statement, myJavaElementTypeContainer.EXPORTS_STATEMENT);
}
private PsiBuilder.Marker parseOpensStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
mapAndAdvance(builder, JavaTokenType.OPENS_KEYWORD);
return parsePackageStatement(builder, statement, myJavaElementTypeContainer.OPENS_STATEMENT);
}
private @NotNull PsiBuilder.Marker parsePackageStatement(PsiBuilder builder, PsiBuilder.Marker statement, IElementType type) {
boolean hasError = false;
if (parseClassOrPackageRef(builder) != null) {
if (JavaKeywords.TO.equals(builder.getTokenText())) {
mapAndAdvance(builder, JavaTokenType.TO_KEYWORD);
while (true) {
PsiBuilder.Marker ref = parseNameRef(builder);
if (!expect(builder, JavaTokenType.COMMA)) {
if (ref == null) hasError = true;
break;
}
}
}
}
else {
error(builder, JavaPsiBundle.message("expected.package.reference"));
hasError = true;
}
if (!hasError) {
semicolon(builder);
}
else {
expect(builder, JavaTokenType.SEMICOLON);
}
statement.done(type);
return statement;
}
private PsiBuilder.Marker parseUsesStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
mapAndAdvance(builder, JavaTokenType.USES_KEYWORD);
if (parseClassOrPackageRef(builder) != null) {
semicolon(builder);
}
else {
error(builder, JavaPsiBundle.message("expected.class.reference"));
expect(builder, JavaTokenType.SEMICOLON);
}
statement.done(myJavaElementTypeContainer.USES_STATEMENT);
return statement;
}
private PsiBuilder.Marker parseProvidesStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
boolean hasError = false;
mapAndAdvance(builder, JavaTokenType.PROVIDES_KEYWORD);
if (parseClassOrPackageRef(builder) == null) {
error(builder, JavaPsiBundle.message("expected.class.reference"));
hasError = true;
}
if (JavaKeywords.WITH.equals(builder.getTokenText())) {
builder.remapCurrentToken(JavaTokenType.WITH_KEYWORD);
hasError = myParser.getReferenceParser()
.parseReferenceList(builder, JavaTokenType.WITH_KEYWORD, myJavaElementTypeContainer.PROVIDES_WITH_LIST, JavaTokenType.COMMA);
}
else if (!hasError) {
IElementType next = builder.getTokenType();
if (next == JavaTokenType.IDENTIFIER && !STATEMENT_KEYWORDS.contains(builder.getTokenText())) {
PsiBuilder.Marker marker = builder.mark();
builder.advanceLexer();
marker.error(JavaPsiBundle.message("expected.with"));
}
else {
error(builder, JavaPsiBundle.message("expected.with"));
}
hasError = true;
}
if (!hasError) {
semicolon(builder);
}
else {
expect(builder, JavaTokenType.SEMICOLON);
}
statement.done(myJavaElementTypeContainer.PROVIDES_STATEMENT);
return statement;
}
private PsiBuilder.Marker parseNameRef(PsiBuilder builder) {
PsiBuilder.Marker name = parseName(builder);
if (name == null) {
error(builder, JavaPsiBundle.message("expected.identifier"));
}
return name;
}
private PsiBuilder.Marker parseClassOrPackageRef(PsiBuilder builder) {
return myParser.getReferenceParser().parseJavaCodeReference(builder, true, false, false, false);
}
private static void mapAndAdvance(PsiBuilder builder, IElementType keyword) {
builder.remapCurrentToken(keyword);
builder.advanceLexer();
}
private static void parseExtras(PsiBuilder builder, @NotNull @NlsContexts.ParsingError String message) {
PsiBuilder.Marker extras = builder.mark();
while (builder.getTokenType() != null) builder.advanceLexer();
extras.error(message);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,21 +1,187 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.java.parser;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.impl.source.AbstractBasicJavaElementTypeFactory;
import com.intellij.psi.impl.source.OldParserWhiteSpaceAndCommentSetHolder;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.lang.PsiBuilderUtil.expect;
import static com.intellij.lang.java.parser.JavaParserUtil.*;
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public class PatternParser extends BasicPatternParser {
public class PatternParser {
private static final TokenSet PATTERN_MODIFIERS = TokenSet.create(JavaTokenType.FINAL_KEYWORD);
private final JavaParser myParser;
private final AbstractBasicJavaElementTypeFactory.JavaElementTypeContainer myJavaElementTypeContainer;
private final OldParserWhiteSpaceAndCommentSetHolder myWhiteSpaceAndCommentSetHolder = OldParserWhiteSpaceAndCommentSetHolder.INSTANCE;
public PatternParser(@NotNull JavaParser javaParser) {
super(javaParser);
this.myParser = javaParser;
this.myJavaElementTypeContainer = (javaParser).getJavaElementTypeFactory().getContainer();
}
@Override
@Contract(pure = true)
public boolean isPattern(PsiBuilder builder) {
return super.isPattern(builder);
PsiBuilder.Marker patternStart = preParsePattern(builder);
if (patternStart == null) {
return false;
}
patternStart.rollbackTo();
return true;
}
private boolean parseUnnamedPattern(final PsiBuilder builder) {
PsiBuilder.Marker patternStart = builder.mark();
if (builder.getTokenType() == JavaTokenType.IDENTIFIER &&
"_".equals(builder.getTokenText())) {
emptyElement(builder, myJavaElementTypeContainer.TYPE);
builder.advanceLexer();
done(patternStart, myJavaElementTypeContainer.UNNAMED_PATTERN, builder, myWhiteSpaceAndCommentSetHolder);
return true;
}
patternStart.rollbackTo();
return false;
}
@Nullable("when not pattern")
PsiBuilder.Marker preParsePattern(final PsiBuilder builder) {
PsiBuilder.Marker patternStart = builder.mark();
Boolean hasNoModifier = myParser.getDeclarationParser().parseModifierList(builder, PATTERN_MODIFIERS).second;
PsiBuilder.Marker type =
myParser.getReferenceParser().parseType(builder, ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD);
boolean isPattern = type != null && (builder.getTokenType() == JavaTokenType.IDENTIFIER ||
(builder.getTokenType() == JavaTokenType.LPARENTH && hasNoModifier));
if (!isPattern) {
patternStart.rollbackTo();
return null;
}
return patternStart;
}
/**
* Must be called only if isPattern returned true
*/
public PsiBuilder.@NotNull Marker parsePattern(final PsiBuilder builder) {
return parsePattern(builder, false);
}
private PsiBuilder.@NotNull Marker parsePattern(final PsiBuilder builder, boolean expectVar) {
return parsePrimaryPattern(builder, expectVar);
}
PsiBuilder.@NotNull Marker parsePrimaryPattern(final PsiBuilder builder, boolean expectVar) {
return parseTypeOrRecordPattern(builder, expectVar);
}
private void parseRecordStructurePattern(final PsiBuilder builder) {
PsiBuilder.Marker recordStructure = builder.mark();
boolean hasLparen = expect(builder, JavaTokenType.LPARENTH);
assert hasLparen;
boolean isFirst = true;
while (builder.getTokenType() != JavaTokenType.RPARENTH) {
if (!isFirst) {
expectOrError(builder, JavaTokenType.COMMA, "expected.comma");
}
if (builder.getTokenType() == null) {
break;
}
if (isPattern(builder)) {
parsePattern(builder, true);
isFirst = false;
}
else if (parseUnnamedPattern(builder)) {
isFirst = false;
}
else {
int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD | ReferenceParser.VAR_TYPE;
myParser.getReferenceParser().parseType(builder, flags);
error(builder, JavaPsiBundle.message("expected.pattern"));
if (builder.getTokenType() == JavaTokenType.RPARENTH) {
break;
}
builder.advanceLexer();
}
}
if (!expect(builder, JavaTokenType.RPARENTH)) {
builder.error(JavaPsiBundle.message("expected.rparen"));
}
recordStructure.done(myJavaElementTypeContainer.DECONSTRUCTION_LIST);
}
private PsiBuilder.@NotNull Marker parseTypeOrRecordPattern(final PsiBuilder builder, boolean expectVar) {
PsiBuilder.Marker pattern = builder.mark();
PsiBuilder.Marker patternVariable = builder.mark();
Boolean hasNoModifiers = myParser.getDeclarationParser().parseModifierList(builder, PATTERN_MODIFIERS).second;
int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD;
if (expectVar) {
flags |= ReferenceParser.VAR_TYPE;
}
PsiBuilder.Marker type = myParser.getReferenceParser().parseType(builder, flags);
assert type != null; // guarded by isPattern
boolean isRecord = false;
if (builder.getTokenType() == JavaTokenType.LPARENTH && hasNoModifiers) {
parseRecordStructurePattern(builder);
isRecord = true;
}
final boolean hasIdentifier;
if (builder.getTokenType() == JavaTokenType.IDENTIFIER &&
(!JavaKeywords.WHEN.equals(builder.getTokenText()) || isWhenAsIdentifier(isRecord))) {
// pattern variable after the record structure pattern
if (isRecord) {
PsiBuilder.Marker variable = builder.mark();
builder.advanceLexer();
variable.done(myJavaElementTypeContainer.DECONSTRUCTION_PATTERN_VARIABLE);
}
else {
builder.advanceLexer();
}
hasIdentifier = true;
}
else {
hasIdentifier = false;
}
if (isRecord) {
patternVariable.drop();
done(pattern, myJavaElementTypeContainer.DECONSTRUCTION_PATTERN, builder, myWhiteSpaceAndCommentSetHolder);
}
else {
if (hasIdentifier) {
done(patternVariable, myJavaElementTypeContainer.PATTERN_VARIABLE, builder, myWhiteSpaceAndCommentSetHolder);
}
else {
patternVariable.drop();
}
done(pattern, myJavaElementTypeContainer.TYPE_TEST_PATTERN, builder, myWhiteSpaceAndCommentSetHolder);
}
return pattern;
}
// There may be valid code samples:
// Rec(int i) when when when.foo() -> {} //now it is unsupported, let's skip it
// ^name ^keyword ^guard expr
//case When when -> {}
// ^name
//case When(when) when when ->{}
// ^keyword ^guard expr
private static boolean isWhenAsIdentifier(boolean previousIsRecord) {
if (previousIsRecord) return false;
return true;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,22 +1,393 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.java.parser;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.PsiBuilder;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.impl.source.AbstractBasicJavaElementTypeFactory;
import com.intellij.psi.impl.source.BasicElementTypes;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.lang.PsiBuilderUtil.expect;
import static com.intellij.lang.java.parser.JavaParserUtil.*;
import static com.intellij.util.BitUtil.isSet;
import static com.intellij.util.BitUtil.set;
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public class ReferenceParser extends BasicReferenceParser {
public static class TypeInfo extends BasicReferenceParser.TypeInfo {
public class ReferenceParser {
public static final int EAT_LAST_DOT = 0x01;
public static final int ELLIPSIS = 0x02;
public static final int WILDCARD = 0x04;
public static final int DIAMONDS = 0x08;
public static final int DISJUNCTIONS = 0x10;
public static final int CONJUNCTIONS = 0x20;
public static final int INCOMPLETE_ANNO = 0x40;
public static final int VAR_TYPE = 0x80;
private static final TokenSet WILDCARD_KEYWORD_SET = TokenSet.create(JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.SUPER_KEYWORD);
private final JavaParser myParser;
private final AbstractBasicJavaElementTypeFactory.JavaElementTypeContainer myJavaElementTypeContainer;
public @Nullable PsiBuilder.Marker parseType(PsiBuilder builder, int flags) {
TypeInfo typeInfo = parseTypeInfo(builder, flags);
return typeInfo != null ? typeInfo.marker : null;
}
private @Nullable TypeInfo parseTypeInfo(PsiBuilder builder, int flags, boolean badWildcard) {
if (builder.getTokenType() == null) return null;
TypeInfo typeInfo = new TypeInfo();
PsiBuilder.Marker type = builder.mark();
PsiBuilder.Marker anno = myParser.getDeclarationParser().parseAnnotations(builder);
IElementType tokenType = builder.getTokenType();
if (tokenType == JavaTokenType.IDENTIFIER &&
isSet(flags, VAR_TYPE) &&
builder.lookAhead(1) != JavaTokenType.DOT &&
builder.lookAhead(1) != JavaTokenType.COLON &&
JavaKeywords.VAR.equals(builder.getTokenText()) &&
JavaFeature.LVTI.isSufficient(getLanguageLevel(builder))) {
builder.remapCurrentToken(tokenType = JavaTokenType.VAR_KEYWORD);
}
else if (tokenType == JavaTokenType.VAR_KEYWORD && !isSet(flags, VAR_TYPE)) {
builder.remapCurrentToken(tokenType = JavaTokenType.IDENTIFIER);
}
if (expect(builder, BasicElementTypes.BASIC_PRIMITIVE_TYPE_BIT_SET)) {
typeInfo.isPrimitive = true;
}
else if ((isSet(flags, WILDCARD) || badWildcard) && (tokenType == JavaTokenType.QUEST)) {
builder.advanceLexer();
completeWildcardType(builder, isSet(flags, WILDCARD), type);
typeInfo.marker = type;
return typeInfo;
}
else if (tokenType == JavaTokenType.IDENTIFIER) {
parseJavaCodeReference(builder, isSet(flags, EAT_LAST_DOT), true, false, false, false, isSet(flags, DIAMONDS), typeInfo);
}
else if (tokenType == JavaTokenType.VAR_KEYWORD) {
builder.advanceLexer();
type.done(myJavaElementTypeContainer.TYPE);
typeInfo.marker = type;
return typeInfo;
}
else if (isSet(flags, DIAMONDS) && tokenType == JavaTokenType.GT) {
if (anno == null) {
emptyElement(builder, myJavaElementTypeContainer.DIAMOND_TYPE);
}
else {
error(builder, JavaPsiBundle.message("expected.identifier"));
typeInfo.hasErrors = true;
}
type.done(myJavaElementTypeContainer.TYPE);
typeInfo.marker = type;
return typeInfo;
}
else {
type.drop();
if (anno != null && isSet(flags, INCOMPLETE_ANNO)) {
error(builder, JavaPsiBundle.message("expected.type"));
typeInfo.marker = anno;
typeInfo.hasErrors = true;
return typeInfo;
}
return null;
}
type.done(myJavaElementTypeContainer.TYPE);
while (true) {
myParser.getDeclarationParser().parseAnnotations(builder);
PsiBuilder.Marker bracket = builder.mark();
if (!expect(builder, JavaTokenType.LBRACKET)) {
bracket.drop();
break;
}
if (!expect(builder, JavaTokenType.RBRACKET)) {
bracket.rollbackTo();
break;
}
bracket.drop();
typeInfo.isArray = true;
}
if (isSet(flags, ELLIPSIS) && builder.getTokenType() == JavaTokenType.ELLIPSIS) {
builder.advanceLexer();
typeInfo.isVarArg = true;
}
if (typeInfo.isVarArg || typeInfo.isArray) {
type = type.precede();
type.done(myJavaElementTypeContainer.TYPE);
}
typeInfo.marker = type;
return typeInfo;
}
private void completeWildcardType(PsiBuilder builder, boolean wildcard, PsiBuilder.Marker type) {
if (expect(builder, WILDCARD_KEYWORD_SET)) {
if (parseTypeInfo(builder, EAT_LAST_DOT) == null) {
error(builder, JavaPsiBundle.message("expected.type"));
}
}
if (wildcard) {
type.done(myJavaElementTypeContainer.TYPE);
}
else {
type.error(JavaPsiBundle.message("error.message.wildcard.not.expected"));
}
}
public @Nullable PsiBuilder.Marker parseJavaCodeReference(PsiBuilder builder,
boolean eatLastDot,
boolean parameterList,
boolean isNew,
boolean diamonds) {
return parseJavaCodeReference(builder, eatLastDot, parameterList, false, false, isNew, diamonds, new TypeInfo());
}
public boolean parseImportCodeReference(PsiBuilder builder, boolean isStatic) {
TypeInfo typeInfo = new TypeInfo();
parseJavaCodeReference(builder, true, false, true, isStatic, false, false, typeInfo);
return !typeInfo.hasErrors;
}
private @Nullable PsiBuilder.Marker parseJavaCodeReference(PsiBuilder builder,
boolean eatLastDot,
boolean parameterList,
boolean isImport,
boolean isStaticImport,
boolean isNew,
boolean diamonds,
TypeInfo typeInfo) {
PsiBuilder.Marker refElement = builder.mark();
myParser.getDeclarationParser().parseAnnotations(builder);
if (!expect(builder, JavaTokenType.IDENTIFIER)) {
refElement.rollbackTo();
if (isImport) {
error(builder, JavaPsiBundle.message("expected.identifier"));
}
typeInfo.hasErrors = true;
return null;
}
if (parameterList) {
typeInfo.isParameterized = parseReferenceParameterList(builder, true, diamonds);
}
else if (!isStaticImport) {
emptyElement(builder, myJavaElementTypeContainer.REFERENCE_PARAMETER_LIST);
}
while (builder.getTokenType() == JavaTokenType.DOT) {
refElement.done(myJavaElementTypeContainer.JAVA_CODE_REFERENCE);
if (isNew && !diamonds && typeInfo.isParameterized) {
return refElement;
}
PsiBuilder.Marker dotPos = builder.mark();
builder.advanceLexer();
myParser.getDeclarationParser().parseAnnotations(builder);
if (isImport && expect(builder, JavaTokenType.ASTERISK)) {
dotPos.drop();
return refElement;
}
else if (!expect(builder, JavaTokenType.IDENTIFIER)) {
if (!eatLastDot) {
dotPos.rollbackTo();
return refElement;
}
typeInfo.hasErrors = true;
if (isImport) {
error(builder, JavaPsiBundle.message("import.statement.identifier.or.asterisk.expected."));
}
else {
error(builder, JavaPsiBundle.message("expected.identifier"));
}
dotPos.drop();
return refElement;
}
dotPos.drop();
refElement = refElement.precede();
if (parameterList) {
typeInfo.isParameterized = parseReferenceParameterList(builder, true, diamonds);
}
else {
emptyElement(builder, myJavaElementTypeContainer.REFERENCE_PARAMETER_LIST);
}
}
if (isStaticImport) {
refElement.done(myJavaElementTypeContainer.IMPORT_STATIC_REFERENCE);
}
else {
refElement.done(myJavaElementTypeContainer.JAVA_CODE_REFERENCE);
}
return refElement;
}
public boolean parseReferenceParameterList(PsiBuilder builder, boolean wildcard, boolean diamonds) {
PsiBuilder.Marker list = builder.mark();
if (!expect(builder, JavaTokenType.LT)) {
list.done(myJavaElementTypeContainer.REFERENCE_PARAMETER_LIST);
return false;
}
int flags = set(set(EAT_LAST_DOT, WILDCARD, wildcard), DIAMONDS, diamonds);
boolean isOk = true;
while (true) {
if (parseTypeInfo(builder, flags, true) == null) {
error(builder, JavaPsiBundle.message("expected.identifier"));
}
else {
IElementType tokenType = builder.getTokenType();
if (WILDCARD_KEYWORD_SET.contains(tokenType)) {
parseReferenceList(builder, tokenType, null, JavaTokenType.AND);
}
}
if (expect(builder, JavaTokenType.GT)) {
break;
}
else if (!expectOrError(builder, JavaTokenType.COMMA, "expected.gt.or.comma")) {
isOk = false;
break;
}
flags = set(flags, DIAMONDS, false);
}
list.done(myJavaElementTypeContainer.REFERENCE_PARAMETER_LIST);
return isOk;
}
public @NotNull PsiBuilder.Marker parseTypeParameters(PsiBuilder builder) {
PsiBuilder.Marker list = builder.mark();
if (!expect(builder, JavaTokenType.LT)) {
list.done(myJavaElementTypeContainer.TYPE_PARAMETER_LIST);
return list;
}
do {
PsiBuilder.Marker param = parseTypeParameter(builder);
if (param == null) {
error(builder, JavaPsiBundle.message("expected.type.parameter"));
}
}
while (expect(builder, JavaTokenType.COMMA));
if (!expect(builder, JavaTokenType.GT)) {
// hack for completion
if (builder.getTokenType() == JavaTokenType.IDENTIFIER) {
if (builder.lookAhead(1) == JavaTokenType.GT) {
PsiBuilder.Marker errorElement = builder.mark();
builder.advanceLexer();
errorElement.error(JavaPsiBundle.message("unexpected.identifier"));
builder.advanceLexer();
}
else {
error(builder, JavaPsiBundle.message("expected.gt"));
}
}
else {
error(builder, JavaPsiBundle.message("expected.gt"));
}
}
list.done(myJavaElementTypeContainer.TYPE_PARAMETER_LIST);
return list;
}
public @Nullable PsiBuilder.Marker parseTypeParameter(PsiBuilder builder) {
PsiBuilder.Marker param = builder.mark();
myParser.getDeclarationParser().parseAnnotations(builder);
boolean wild = expect(builder, JavaTokenType.QUEST);
if (!wild && !expect(builder, JavaTokenType.IDENTIFIER)) {
param.rollbackTo();
return null;
}
parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, myJavaElementTypeContainer.EXTENDS_BOUND_LIST, JavaTokenType.AND);
if (!wild) {
param.done(myJavaElementTypeContainer.TYPE_PARAMETER);
}
else {
param.error(JavaPsiBundle.message("error.message.wildcard.not.expected"));
}
return param;
}
public boolean parseReferenceList(PsiBuilder builder, IElementType start, @Nullable IElementType type, IElementType delimiter) {
PsiBuilder.Marker element = builder.mark();
boolean endsWithError = false;
if (expect(builder, start)) {
do {
endsWithError = false;
PsiBuilder.Marker classReference = parseJavaCodeReference(builder, false, true, false, false);
if (classReference == null) {
error(builder, JavaPsiBundle.message("expected.identifier"));
endsWithError = true;
}
}
while (expect(builder, delimiter));
}
if (type != null) {
element.done(type);
}
else {
element.error(JavaPsiBundle.message("bound.not.expected"));
}
return endsWithError;
}
//for backward compatibility
@Override
@Nullable
public ReferenceParser.TypeInfo parseTypeInfo(PsiBuilder builder, int flags) {
BasicReferenceParser.TypeInfo typeInfo = super.parseTypeInfo(builder, flags);
ReferenceParser.TypeInfo typeInfo1 = parseTypeInfo(builder, flags, false);
if (typeInfo1 != null) {
assert !isSet(flags, DISJUNCTIONS) || !isSet(flags, CONJUNCTIONS) : "don't set both flags simultaneously";
IElementType operator = isSet(flags, DISJUNCTIONS) ? JavaTokenType.OR : isSet(flags, CONJUNCTIONS) ? JavaTokenType.AND : null;
if (operator != null && builder.getTokenType() == operator) {
typeInfo1.marker = typeInfo1.marker.precede();
while (builder.getTokenType() == operator) {
builder.advanceLexer();
IElementType tokenType = builder.getTokenType();
if (tokenType != JavaTokenType.IDENTIFIER && tokenType != JavaTokenType.AT) {
error(builder, JavaPsiBundle.message("expected.identifier"));
}
parseTypeInfo(builder, flags, false);
}
typeInfo1.marker.done(myJavaElementTypeContainer.TYPE);
}
}
TypeInfo typeInfo = typeInfo1;
if (typeInfo == null) {
return null;
}
@@ -31,6 +402,16 @@ public class ReferenceParser extends BasicReferenceParser {
}
public ReferenceParser(@NotNull JavaParser javaParser) {
super(javaParser);
this.myParser = javaParser;
this.myJavaElementTypeContainer = javaParser.getJavaElementTypeFactory().getContainer();
}
public static class TypeInfo {
public boolean isPrimitive;
public boolean isParameterized;
public boolean isArray;
public boolean isVarArg;
public boolean hasErrors;
public PsiBuilder.Marker marker;
}
}
@@ -1,16 +1,847 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.java.parser;
import com.intellij.core.JavaPsiBundle;
import com.intellij.java.syntax.parser.JavaKeywords;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.WhitespacesBinders;
import com.intellij.openapi.util.Pair;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.impl.source.AbstractBasicJavaElementTypeFactory;
import com.intellij.psi.impl.source.OldParserWhiteSpaceAndCommentSetHolder;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ILazyParseableElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import static com.intellij.lang.PsiBuilderUtil.*;
import static com.intellij.lang.java.parser.JavaParserUtil.*;
import static com.intellij.psi.impl.source.tree.ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
/**
* @deprecated Use the new Java syntax library instead.
* See {@link com.intellij.java.syntax.parser.JavaParser}
* See {@link com.intellij.java.syntax.parser.JavaParser}
*/
@Deprecated
public class StatementParser extends BasicStatementParser {
public class StatementParser {
private static final TokenSet YIELD_STMT_INDICATOR_TOKENS = TokenSet.create(
JavaTokenType.PLUS, JavaTokenType.MINUS, JavaTokenType.EXCL, JavaTokenType.TILDE,
JavaTokenType.SUPER_KEYWORD, JavaTokenType.THIS_KEYWORD,
JavaTokenType.TRUE_KEYWORD, JavaTokenType.FALSE_KEYWORD, JavaTokenType.NULL_KEYWORD,
JavaTokenType.STRING_LITERAL, JavaTokenType.INTEGER_LITERAL, JavaTokenType.DOUBLE_LITERAL,
JavaTokenType.FLOAT_LITERAL, JavaTokenType.LONG_LITERAL, JavaTokenType.CHARACTER_LITERAL,
JavaTokenType.TEXT_BLOCK_LITERAL,
JavaTokenType.IDENTIFIER, JavaTokenType.SWITCH_KEYWORD, JavaTokenType.NEW_KEYWORD,
JavaTokenType.LPARENTH,
// recovery
JavaTokenType.RBRACE, JavaTokenType.SEMICOLON, JavaTokenType.CASE_KEYWORD
);
private static final TokenSet TRY_CLOSERS_SET = TokenSet.create(JavaTokenType.CATCH_KEYWORD, JavaTokenType.FINALLY_KEYWORD);
private final JavaParser myParser;
private final AbstractBasicJavaElementTypeFactory.JavaElementTypeContainer myJavaElementTypeContainer;
private final OldParserWhiteSpaceAndCommentSetHolder myWhiteSpaceAndCommentSetHolder = OldParserWhiteSpaceAndCommentSetHolder.INSTANCE;
public StatementParser(@NotNull JavaParser javaParser) {
super(javaParser);
this.myParser = javaParser;
this.myJavaElementTypeContainer = javaParser.getJavaElementTypeFactory().getContainer();
}
public @Nullable PsiBuilder.Marker parseCodeBlock(@NotNull PsiBuilder builder) {
return parseCodeBlock(builder, false);
}
public @Nullable PsiBuilder.Marker parseCodeBlock(@NotNull PsiBuilder builder, boolean isStatement) {
if (builder.getTokenType() != JavaTokenType.LBRACE) return null;
if (isStatement && isParseStatementCodeBlocksDeep(builder)) return parseCodeBlockDeep(builder, false);
return parseBlockLazy(builder, JavaTokenType.LBRACE, JavaTokenType.RBRACE, myJavaElementTypeContainer.CODE_BLOCK);
}
public @Nullable PsiBuilder.Marker parseCodeBlockDeep(@NotNull PsiBuilder builder, boolean parseUntilEof) {
if (builder.getTokenType() != JavaTokenType.LBRACE) return null;
PsiBuilder.Marker codeBlock = builder.mark();
builder.advanceLexer();
parseStatements(builder, parseUntilEof ? BraceMode.TILL_LAST : BraceMode.TILL_FIRST);
boolean greedyBlock = !expectOrError(builder, JavaTokenType.RBRACE, "expected.rbrace");
builder.getTokenType(); // eat spaces
done(codeBlock, myJavaElementTypeContainer.CODE_BLOCK, builder, myWhiteSpaceAndCommentSetHolder);
if (greedyBlock) {
codeBlock.setCustomEdgeTokenBinders(null, WhitespacesBinders.GREEDY_RIGHT_BINDER);
}
return codeBlock;
}
public void parseStatements(@NotNull PsiBuilder builder) {
parseStatements(builder, null);
}
private void parseStatements(PsiBuilder builder, @Nullable BraceMode braceMode) {
while (builder.getTokenType() != null) {
PsiBuilder.Marker statement = parseStatement(builder);
if (statement != null) continue;
IElementType tokenType = builder.getTokenType();
if (tokenType == JavaTokenType.RBRACE &&
(braceMode == BraceMode.TILL_FIRST || braceMode == BraceMode.TILL_LAST && builder.lookAhead(1) == null)) {
break;
}
PsiBuilder.Marker error = builder.mark();
builder.advanceLexer();
if (tokenType == JavaTokenType.ELSE_KEYWORD) {
error.error(JavaPsiBundle.message("else.without.if"));
}
else if (tokenType == JavaTokenType.CATCH_KEYWORD) {
error.error(JavaPsiBundle.message("catch.without.try"));
}
else if (tokenType == JavaTokenType.FINALLY_KEYWORD) {
error.error(JavaPsiBundle.message("finally.without.try"));
}
else {
error.error(JavaPsiBundle.message("unexpected.token"));
}
}
}
public @Nullable PsiBuilder.Marker parseStatement(@NotNull PsiBuilder builder) {
IElementType tokenType = builder.getTokenType();
if (tokenType == JavaTokenType.IF_KEYWORD) {
return parseIfStatement(builder);
}
else if (tokenType == JavaTokenType.WHILE_KEYWORD) {
return parseWhileStatement(builder);
}
else if (tokenType == JavaTokenType.FOR_KEYWORD) {
return parseForStatement(builder);
}
else if (tokenType == JavaTokenType.DO_KEYWORD) {
return parseDoWhileStatement(builder);
}
else if (tokenType == JavaTokenType.SWITCH_KEYWORD) {
return parseSwitchStatement(builder);
}
else if (tokenType == JavaTokenType.CASE_KEYWORD || tokenType == JavaTokenType.DEFAULT_KEYWORD) {
return parseSwitchLabelStatement(builder);
}
else if (tokenType == JavaTokenType.BREAK_KEYWORD) {
return parseBreakStatement(builder);
}
else if (isStmtYieldToken(builder, tokenType)) {
return parseYieldStatement(builder);
}
else if (tokenType == JavaTokenType.CONTINUE_KEYWORD) {
return parseContinueStatement(builder);
}
else if (tokenType == JavaTokenType.RETURN_KEYWORD) {
return parseReturnStatement(builder);
}
else if (tokenType == JavaTokenType.THROW_KEYWORD) {
return parseThrowStatement(builder);
}
else if (tokenType == JavaTokenType.SYNCHRONIZED_KEYWORD) {
return parseSynchronizedStatement(builder);
}
else if (tokenType == JavaTokenType.TRY_KEYWORD) {
return parseTryStatement(builder);
}
else if (tokenType == JavaTokenType.ASSERT_KEYWORD) {
return parseAssertStatement(builder);
}
else if (tokenType == JavaTokenType.LBRACE) {
return parseBlockStatement(builder);
}
else if (tokenType instanceof ILazyParseableElementType) {
builder.advanceLexer();
return null;
}
else if (tokenType == JavaTokenType.SEMICOLON) {
PsiBuilder.Marker empty = builder.mark();
builder.advanceLexer();
done(empty, myJavaElementTypeContainer.EMPTY_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return empty;
}
else if (tokenType == JavaTokenType.IDENTIFIER || tokenType == JavaTokenType.AT) {
PsiBuilder.Marker refPos = builder.mark();
boolean nonSealed = DeclarationParser.isNonSealedToken(builder, tokenType);
myParser.getDeclarationParser().parseAnnotations(builder);
skipQualifiedName(builder);
IElementType current = builder.getTokenType(), next = builder.lookAhead(1);
refPos.rollbackTo();
if (current == JavaTokenType.LT || current == JavaTokenType.DOT && next == JavaTokenType.AT || nonSealed) {
PsiBuilder.Marker declStatement = builder.mark();
if (myParser.getDeclarationParser().parse(builder, DeclarationParser.Context.CODE_BLOCK) != null) {
done(declStatement, myJavaElementTypeContainer.DECLARATION_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return declStatement;
}
ReferenceParser.TypeInfo type = myParser.getReferenceParser().parseTypeInfo(builder, 0);
if (current == JavaTokenType.LT && (type == null || !type.isParameterized)) {
declStatement.rollbackTo();
}
else if (type == null || builder.getTokenType() != JavaTokenType.DOUBLE_COLON) {
error(builder, JavaPsiBundle.message("expected.identifier"));
if (type == null) builder.advanceLexer();
done(declStatement, myJavaElementTypeContainer.DECLARATION_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return declStatement;
}
else {
declStatement.rollbackTo(); // generic type followed by the double colon is a good candidate for being a constructor reference
}
}
}
PsiBuilder.Marker pos = builder.mark();
PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder);
IElementType incompleteDeclarationRestrictedTokenType = null;
if (expr != null) {
int count = 1;
PsiBuilder.Marker list = expr.precede();
PsiBuilder.Marker statement = list.precede();
while (builder.getTokenType() == JavaTokenType.COMMA) {
PsiBuilder.Marker commaPos = builder.mark();
builder.advanceLexer();
PsiBuilder.Marker expr1 = myParser.getExpressionParser().parse(builder);
if (expr1 == null) {
commaPos.rollbackTo();
break;
}
commaPos.drop();
count++;
}
if (count > 1) {
pos.drop();
done(list, myJavaElementTypeContainer.EXPRESSION_LIST, builder, myWhiteSpaceAndCommentSetHolder);
semicolon(builder);
done(statement, myJavaElementTypeContainer.EXPRESSION_LIST_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
if (exprType(expr) != myJavaElementTypeContainer.REFERENCE_EXPRESSION) {
drop(list, pos);
semicolon(builder);
done(statement, myJavaElementTypeContainer.EXPRESSION_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
boolean singleToken = expr.getEndIndex() - expr.getStartIndex() == 1;
pos.rollbackTo();
if (singleToken && builder.getTokenType() == JavaTokenType.IDENTIFIER) {
String text = builder.getTokenText();
if (JavaKeywords.RECORD.equals(text) && JavaFeature.RECORDS.isSufficient(getLanguageLevel(builder))) {
incompleteDeclarationRestrictedTokenType = JavaTokenType.RECORD_KEYWORD;
}
if (JavaKeywords.VAR.equals(text) && JavaFeature.LVTI.isSufficient(getLanguageLevel(builder))) {
incompleteDeclarationRestrictedTokenType = JavaTokenType.VAR_KEYWORD;
}
}
}
else {
pos.drop();
}
PsiBuilder.Marker decl = myParser.getDeclarationParser().parse(builder, DeclarationParser.Context.CODE_BLOCK);
if (decl != null) {
PsiBuilder.Marker statement = decl.precede();
done(statement, myJavaElementTypeContainer.DECLARATION_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
if (builder.getTokenType() == JavaTokenType.IDENTIFIER && builder.lookAhead(1) == JavaTokenType.COLON) {
PsiBuilder.Marker statement = builder.mark();
advance(builder, 2);
parseStatement(builder);
done(statement, myJavaElementTypeContainer.LABELED_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
if (expr != null) {
PsiBuilder.Marker statement = builder.mark();
IElementType statementType;
if (incompleteDeclarationRestrictedTokenType != null) {
builder.remapCurrentToken(incompleteDeclarationRestrictedTokenType);
builder.advanceLexer();
error(builder, JavaPsiBundle.message("expected.identifier"));
statementType = myJavaElementTypeContainer.DECLARATION_STATEMENT;
}
else {
myParser.getExpressionParser().parse(builder);
statementType = myJavaElementTypeContainer.EXPRESSION_STATEMENT;
semicolon(builder);
}
done(statement, statementType, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
return null;
}
private @NotNull PsiBuilder.Marker parseIfStatement(PsiBuilder builder) {
ArrayList<PsiBuilder.Marker> stack = null;
PsiBuilder.Marker statement;
while (true) {
// replaced recursion with iteration plus stack to avoid huge call stack for extremely large else-if chains
statement = builder.mark();
builder.advanceLexer();
if (parseExprInParenth(builder)) {
if (parseStatement(builder) == null) {
error(builder, JavaPsiBundle.message("expected.statement"));
}
else if (expect(builder, JavaTokenType.ELSE_KEYWORD)) {
if (builder.getTokenType() == JavaTokenType.IF_KEYWORD) {
if (stack == null) stack = new ArrayList<>();
stack.add(statement);
continue;
}
else if (parseStatement(builder) == null) {
error(builder, JavaPsiBundle.message("expected.statement"));
}
}
}
break;
}
done(statement, myJavaElementTypeContainer.IF_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
if (stack != null) {
for (int i = stack.size() - 1; i >= 0; i--) {
statement = stack.get(i);
done(statement, myJavaElementTypeContainer.IF_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
}
}
return statement;
}
private @NotNull PsiBuilder.Marker parseWhileStatement(PsiBuilder builder) {
return parseExprInParenthWithBlock(builder, myJavaElementTypeContainer.WHILE_STATEMENT, false);
}
@Contract(pure = true)
private boolean isRecordPatternInForEach(final PsiBuilder builder) {
PsiBuilder.Marker patternStart = myParser.getPatternParser().preParsePattern(builder);
if (patternStart == null) {
return false;
}
if (builder.getTokenType() != JavaTokenType.LPARENTH) {
patternStart.rollbackTo();
return false;
}
builder.advanceLexer();
// we must distinguish a record pattern from method call in for (foo();;)
int parenBalance = 1;
while (true) {
IElementType current = builder.getTokenType();
if (current == null) {
patternStart.rollbackTo();
return false;
}
if (current == JavaTokenType.LPARENTH) {
parenBalance++;
}
if (current == JavaTokenType.RPARENTH) {
parenBalance--;
if (parenBalance == 0) {
break;
}
}
builder.advanceLexer();
}
builder.advanceLexer();
boolean isRecordPattern = builder.getTokenType() != JavaTokenType.SEMICOLON && builder.getTokenType() != JavaTokenType.DOT;
patternStart.rollbackTo();
return isRecordPattern;
}
private @NotNull PsiBuilder.Marker parseForStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
if (!expect(builder, JavaTokenType.LPARENTH)) {
error(builder, JavaPsiBundle.message("expected.lparen"));
done(statement, myJavaElementTypeContainer.FOR_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
if (isRecordPatternInForEach(builder)) {
myParser.getPatternParser().parsePattern(builder);
if (builder.getTokenType() == JavaTokenType.COLON) {
return parseForEachFromColon(builder, statement, myJavaElementTypeContainer.FOREACH_PATTERN_STATEMENT);
}
error(builder, JavaPsiBundle.message("expected.colon"));
// recovery: just skip everything until ')'
while (true) {
IElementType tokenType = builder.getTokenType();
if (tokenType == null) {
done(statement, myJavaElementTypeContainer.FOREACH_PATTERN_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
if (tokenType == JavaTokenType.RPARENTH) {
return parserForEachFromRparenth(builder, statement, myJavaElementTypeContainer.FOREACH_PATTERN_STATEMENT);
}
builder.advanceLexer();
}
}
PsiBuilder.Marker afterParenth = builder.mark();
PsiBuilder.Marker param = myParser.getDeclarationParser().parseParameter(builder, false, false, true);
if (param == null || exprType(param) != myJavaElementTypeContainer.PARAMETER || builder.getTokenType() != JavaTokenType.COLON) {
afterParenth.rollbackTo();
return parseForLoopFromInitializer(builder, statement);
}
else {
afterParenth.drop();
return parseForEachFromColon(builder, statement, myJavaElementTypeContainer.FOREACH_STATEMENT);
}
}
private @NotNull PsiBuilder.Marker parseForLoopFromInitializer(PsiBuilder builder, PsiBuilder.Marker statement) {
if (parseStatement(builder) == null) {
error(builder, JavaPsiBundle.message("expected.statement"));
if (!expect(builder, JavaTokenType.RPARENTH)) {
done(statement, myJavaElementTypeContainer.FOR_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
}
else {
boolean missingSemicolon = false;
if (getLastToken(builder) != JavaTokenType.SEMICOLON) {
missingSemicolon = !expectOrError(builder, JavaTokenType.SEMICOLON, "expected.semicolon");
}
PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder);
missingSemicolon &= expr == null;
if (!expect(builder, JavaTokenType.SEMICOLON)) {
if (!missingSemicolon) {
error(builder, JavaPsiBundle.message("expected.semicolon"));
}
if (!expect(builder, JavaTokenType.RPARENTH)) {
done(statement, myJavaElementTypeContainer.FOR_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
}
else {
parseForUpdateExpressions(builder);
if (!expect(builder, JavaTokenType.RPARENTH)) {
error(builder, JavaPsiBundle.message("expected.rparen"));
done(statement, myJavaElementTypeContainer.FOR_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
}
}
PsiBuilder.Marker bodyStatement = parseStatement(builder);
if (bodyStatement == null) {
error(builder, JavaPsiBundle.message("expected.statement"));
}
done(statement, myJavaElementTypeContainer.FOR_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private void parseForUpdateExpressions(PsiBuilder builder) {
PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder);
if (expr == null) return;
PsiBuilder.Marker expressionStatement;
if (builder.getTokenType() != JavaTokenType.COMMA) {
expressionStatement = expr.precede();
done(expressionStatement, myJavaElementTypeContainer.EXPRESSION_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
}
else {
PsiBuilder.Marker expressionList = expr.precede();
expressionStatement = expressionList.precede();
do {
builder.advanceLexer();
PsiBuilder.Marker nextExpression = myParser.getExpressionParser().parse(builder);
if (nextExpression == null) {
error(builder, JavaPsiBundle.message("expected.expression"));
}
}
while (builder.getTokenType() == JavaTokenType.COMMA);
done(expressionList, myJavaElementTypeContainer.EXPRESSION_LIST, builder, myWhiteSpaceAndCommentSetHolder);
done(expressionStatement, myJavaElementTypeContainer.EXPRESSION_LIST_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
}
expressionStatement.setCustomEdgeTokenBinders(null, WhitespacesBinders.DEFAULT_RIGHT_BINDER);
}
private @NotNull PsiBuilder.Marker parseForEachFromColon(PsiBuilder builder, PsiBuilder.Marker statement, IElementType foreachStatement) {
builder.advanceLexer();
if (myParser.getExpressionParser().parse(builder) == null) {
error(builder, JavaPsiBundle.message("expected.expression"));
}
return parserForEachFromRparenth(builder, statement, foreachStatement);
}
private PsiBuilder.Marker parserForEachFromRparenth(PsiBuilder builder, PsiBuilder.Marker statement, IElementType forEachType) {
if (expectOrError(builder, JavaTokenType.RPARENTH, "expected.rparen") && parseStatement(builder) == null) {
error(builder, JavaPsiBundle.message("expected.statement"));
}
done(statement, forEachType, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull PsiBuilder.Marker parseDoWhileStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
PsiBuilder.Marker body = parseStatement(builder);
if (body == null) {
error(builder, JavaPsiBundle.message("expected.statement"));
}
else if (!expect(builder, JavaTokenType.WHILE_KEYWORD)) {
error(builder, JavaPsiBundle.message("expected.while"));
}
else if (parseExprInParenth(builder)) {
semicolon(builder);
}
done(statement, myJavaElementTypeContainer.DO_WHILE_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull PsiBuilder.Marker parseSwitchStatement(PsiBuilder builder) {
return parseExprInParenthWithBlock(builder, myJavaElementTypeContainer.SWITCH_STATEMENT, true);
}
/**
* @return marker and whether it contains expression inside
*/
@ApiStatus.Internal
public @NotNull Pair<PsiBuilder.@Nullable Marker, Boolean> parseCaseLabel(PsiBuilder builder) {
if (builder.getTokenType() == JavaTokenType.DEFAULT_KEYWORD) {
PsiBuilder.Marker defaultElement = builder.mark();
builder.advanceLexer();
done(defaultElement, myJavaElementTypeContainer.DEFAULT_CASE_LABEL_ELEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return Pair.create(defaultElement, false);
}
if (myParser.getPatternParser().isPattern(builder)) {
PsiBuilder.Marker pattern = myParser.getPatternParser().parsePattern(builder);
return Pair.create(pattern, false);
}
return Pair.create(myParser.getExpressionParser().parseAssignmentForbiddingLambda(builder), true);
}
private PsiBuilder.Marker parseSwitchLabelStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
boolean isCase = builder.getTokenType() == JavaTokenType.CASE_KEYWORD;
builder.advanceLexer();
if (isCase) {
boolean patternsAllowed = JavaFeature.PATTERNS_IN_SWITCH.isSufficient(getLanguageLevel(builder));
PsiBuilder.Marker list = builder.mark();
do {
Pair<PsiBuilder.Marker, Boolean> markerAndIsExpression = parseCaseLabel(builder);
PsiBuilder.Marker caseLabel = markerAndIsExpression.first;
if (caseLabel == null) {
error(builder, JavaPsiBundle.message(patternsAllowed ? "expected.case.label.element" : "expected.expression"));
}
}
while (expect(builder, JavaTokenType.COMMA));
done(list, myJavaElementTypeContainer.CASE_LABEL_ELEMENT_LIST, builder, myWhiteSpaceAndCommentSetHolder);
parseGuard(builder);
}
if (expect(builder, JavaTokenType.ARROW)) {
PsiBuilder.Marker expr;
if (builder.getTokenType() == JavaTokenType.LBRACE) {
PsiBuilder.Marker body = builder.mark();
parseCodeBlock(builder, true);
body.done(myJavaElementTypeContainer.BLOCK_STATEMENT);
if (builder.getTokenType() == JavaTokenType.SEMICOLON) {
PsiBuilder.Marker mark = builder.mark();
while (builder.getTokenType() == JavaTokenType.SEMICOLON) builder.advanceLexer();
mark.error(JavaPsiBundle.message("expected.switch.label"));
}
}
else if (builder.getTokenType() == JavaTokenType.THROW_KEYWORD) {
parseThrowStatement(builder);
}
else if ((expr = myParser.getExpressionParser().parse(builder)) != null) {
PsiBuilder.Marker body = expr.precede();
semicolon(builder);
body.done(myJavaElementTypeContainer.EXPRESSION_STATEMENT);
}
else {
error(builder, JavaPsiBundle.message("expected.switch.rule"));
expect(builder, JavaTokenType.SEMICOLON);
}
done(statement, myJavaElementTypeContainer.SWITCH_LABELED_RULE, builder, myWhiteSpaceAndCommentSetHolder);
}
else {
expectOrError(builder, JavaTokenType.COLON, "expected.colon.or.arrow");
done(statement, myJavaElementTypeContainer.SWITCH_LABEL_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
}
return statement;
}
private void parseGuard(PsiBuilder builder) {
if (builder.getTokenType() == JavaTokenType.IDENTIFIER && JavaKeywords.WHEN.equals(builder.getTokenText())) {
builder.remapCurrentToken(JavaTokenType.WHEN_KEYWORD);
builder.advanceLexer();
PsiBuilder.Marker guardingExpression = myParser.getExpressionParser().parseAssignmentForbiddingLambda(builder);
if (guardingExpression == null) {
error(builder, JavaPsiBundle.message("expected.expression"));
}
}
}
private @NotNull PsiBuilder.Marker parseBreakStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
expect(builder, JavaTokenType.IDENTIFIER);
semicolon(builder);
done(statement, myJavaElementTypeContainer.BREAK_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull PsiBuilder.Marker parseYieldStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.remapCurrentToken(JavaTokenType.YIELD_KEYWORD);
builder.advanceLexer();
if (myParser.getExpressionParser().parse(builder) == null) {
error(builder, JavaPsiBundle.message("expected.expression"));
}
else {
semicolon(builder);
}
done(statement, myJavaElementTypeContainer.YIELD_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull PsiBuilder.Marker parseContinueStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
expect(builder, JavaTokenType.IDENTIFIER);
semicolon(builder);
done(statement, myJavaElementTypeContainer.CONTINUE_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull PsiBuilder.Marker parseReturnStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
myParser.getExpressionParser().parse(builder);
semicolon(builder);
done(statement, myJavaElementTypeContainer.RETURN_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull PsiBuilder.Marker parseThrowStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
if (myParser.getExpressionParser().parse(builder) == null) {
error(builder, JavaPsiBundle.message("expected.expression"));
}
else {
semicolon(builder);
}
done(statement, myJavaElementTypeContainer.THROW_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull PsiBuilder.Marker parseSynchronizedStatement(PsiBuilder builder) {
return parseExprInParenthWithBlock(builder, myJavaElementTypeContainer.SYNCHRONIZED_STATEMENT, true);
}
private @NotNull PsiBuilder.Marker parseTryStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
boolean hasResourceList = builder.getTokenType() == JavaTokenType.LPARENTH;
if (hasResourceList) {
myParser.getDeclarationParser().parseResourceList(builder);
}
PsiBuilder.Marker tryBlock = parseCodeBlock(builder, true);
if (tryBlock == null) {
error(builder, JavaPsiBundle.message("expected.lbrace"));
}
else if (!hasResourceList && !TRY_CLOSERS_SET.contains(builder.getTokenType())) {
error(builder, JavaPsiBundle.message("expected.catch.or.finally"));
}
else {
while (builder.getTokenType() == JavaTokenType.CATCH_KEYWORD) {
if (!parseCatchBlock(builder)) break;
}
if (expect(builder, JavaTokenType.FINALLY_KEYWORD)) {
PsiBuilder.Marker finallyBlock = parseCodeBlock(builder, true);
if (finallyBlock == null) {
error(builder, JavaPsiBundle.message("expected.lbrace"));
}
}
}
done(statement, myJavaElementTypeContainer.TRY_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
public boolean parseCatchBlock(@NotNull PsiBuilder builder) {
assert builder.getTokenType() == JavaTokenType.CATCH_KEYWORD : builder.getTokenType();
PsiBuilder.Marker section = builder.mark();
builder.advanceLexer();
if (!expect(builder, JavaTokenType.LPARENTH)) {
error(builder, JavaPsiBundle.message("expected.lparen"));
done(section, myJavaElementTypeContainer.CATCH_SECTION, builder, myWhiteSpaceAndCommentSetHolder);
return false;
}
PsiBuilder.Marker param = myParser.getDeclarationParser().parseParameter(builder, false, true, false);
if (param == null) {
error(builder, JavaPsiBundle.message("expected.parameter"));
}
if (!expect(builder, JavaTokenType.RPARENTH)) {
error(builder, JavaPsiBundle.message("expected.rparen"));
done(section, myJavaElementTypeContainer.CATCH_SECTION, builder, myWhiteSpaceAndCommentSetHolder);
return false;
}
PsiBuilder.Marker body = parseCodeBlock(builder, true);
if (body == null) {
error(builder, JavaPsiBundle.message("expected.lbrace"));
done(section, myJavaElementTypeContainer.CATCH_SECTION, builder, myWhiteSpaceAndCommentSetHolder);
return false;
}
done(section, myJavaElementTypeContainer.CATCH_SECTION, builder, myWhiteSpaceAndCommentSetHolder);
return true;
}
private @NotNull PsiBuilder.Marker parseAssertStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
if (myParser.getExpressionParser().parse(builder) == null) {
error(builder, JavaPsiBundle.message("expected.boolean.expression"));
}
else if (expect(builder, JavaTokenType.COLON) && myParser.getExpressionParser().parse(builder) == null) {
error(builder, JavaPsiBundle.message("expected.expression"));
}
else {
semicolon(builder);
}
done(statement, myJavaElementTypeContainer.ASSERT_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private @NotNull PsiBuilder.Marker parseBlockStatement(PsiBuilder builder) {
PsiBuilder.Marker statement = builder.mark();
parseCodeBlock(builder, true);
done(statement, myJavaElementTypeContainer.BLOCK_STATEMENT, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
public @NotNull PsiBuilder.Marker parseExprInParenthWithBlock(@NotNull PsiBuilder builder, @NotNull IElementType type, boolean block) {
PsiBuilder.Marker statement = builder.mark();
builder.advanceLexer();
if (parseExprInParenth(builder)) {
PsiBuilder.Marker body = block ? parseCodeBlock(builder, true) : parseStatement(builder);
if (body == null) {
error(builder, JavaPsiBundle.message(block ? "expected.lbrace" : "expected.statement"));
}
}
done(statement, type, builder, myWhiteSpaceAndCommentSetHolder);
return statement;
}
private boolean parseExprInParenth(PsiBuilder builder) {
if (!expect(builder, JavaTokenType.LPARENTH)) {
error(builder, JavaPsiBundle.message("expected.lparen"));
return false;
}
PsiBuilder.Marker beforeExpr = builder.mark();
PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder);
if (expr == null || builder.getTokenType() == JavaTokenType.SEMICOLON) {
beforeExpr.rollbackTo();
error(builder, JavaPsiBundle.message("expected.expression"));
if (builder.getTokenType() != JavaTokenType.RPARENTH) {
return false;
}
}
else {
beforeExpr.drop();
if (builder.getTokenType() != JavaTokenType.RPARENTH) {
error(builder, JavaPsiBundle.message("expected.rparen"));
return false;
}
}
builder.advanceLexer();
return true;
}
private static boolean isStmtYieldToken(@NotNull PsiBuilder builder, IElementType tokenType) {
if (!(tokenType == JavaTokenType.IDENTIFIER &&
JavaKeywords.YIELD.equals(builder.getTokenText()) &&
JavaFeature.SWITCH_EXPRESSION.isSufficient(getLanguageLevel(builder)))) {
return false;
}
// we prefer to parse it as yield stmt wherever possible (even in incomplete syntax)
PsiBuilder.Marker maybeYieldStmt = builder.mark();
builder.advanceLexer();
IElementType tokenAfterYield = builder.getTokenType();
if (tokenAfterYield == null || YIELD_STMT_INDICATOR_TOKENS.contains(tokenAfterYield)) {
maybeYieldStmt.rollbackTo();
return true;
}
if (JavaTokenType.PLUSPLUS.equals(tokenAfterYield) || JavaTokenType.MINUSMINUS.equals(tokenAfterYield)) {
builder.advanceLexer();
boolean isYieldStmt = !builder.getTokenType().equals(JavaTokenType.SEMICOLON);
maybeYieldStmt.rollbackTo();
return isYieldStmt;
}
maybeYieldStmt.rollbackTo();
return false;
}
private static void skipQualifiedName(PsiBuilder builder) {
if (!expect(builder, JavaTokenType.IDENTIFIER)) return;
while (builder.getTokenType() == JavaTokenType.DOT && builder.lookAhead(1) == JavaTokenType.IDENTIFIER) {
advance(builder, 2);
}
}
private static IElementType getLastToken(PsiBuilder builder) {
IElementType token;
int offset = -1;
while (JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains((token = builder.rawLookup(offset)))) offset--;
return token;
}
private enum BraceMode {
TILL_FIRST, TILL_LAST
}
}
@@ -15,9 +15,12 @@
*/
package com.intellij.psi.impl.source;
import com.intellij.lang.java.parser.BasicJavaParserUtil;
import com.intellij.java.syntax.element.lazyParser.IncompleteFragmentParsingException;
import com.intellij.lang.java.parser.JavaParserUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.impl.source.tree.FileElement;
import com.intellij.psi.impl.source.tree.JavaElementTypeFactory;
import com.intellij.psi.impl.source.tree.TreeElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -25,17 +28,74 @@ import org.jetbrains.annotations.Nullable;
* Dummy file element for using together with DummyHolder.
* See {@link com.intellij.psi.impl.PsiJavaParserFacadeImpl} for details.
*/
public class JavaDummyElement extends BasicJavaDummyElement {
public class JavaDummyElement extends FileElement {
private final JavaParserUtil.ParserWrapper myParser;
private final LanguageLevel myLanguageLevel;
private final boolean myConsumeAll;
private Throwable myParserError;
public JavaDummyElement(@Nullable CharSequence text,
BasicJavaParserUtil.@NotNull ParserWrapper parser,
JavaParserUtil.@NotNull ParserWrapper parser,
@NotNull LanguageLevel level) {
super(text, parser, level, JavaElementTypeFactory.INSTANCE);
this(text, parser, level, JavaElementTypeFactory.INSTANCE, false);
}
public JavaDummyElement(@Nullable CharSequence text,
BasicJavaParserUtil.@NotNull ParserWrapper parser,
JavaParserUtil.@NotNull ParserWrapper parser,
@NotNull LanguageLevel level,
boolean consumeAll) {
super(text, parser, level, JavaElementTypeFactory.INSTANCE, consumeAll);
super(JavaElementTypeFactory.INSTANCE.getContainer().DUMMY_ELEMENT, text);
this.myParser = parser;
this.myLanguageLevel = level;
this.myConsumeAll = consumeAll;
}
public JavaDummyElement(@Nullable CharSequence text,
@NotNull JavaParserUtil.ParserWrapper parser,
@NotNull LanguageLevel level,
@NotNull AbstractBasicJavaElementTypeFactory javaElementTypeFactory,
boolean consumeAll) {
super(javaElementTypeFactory.getContainer().DUMMY_ELEMENT, text);
myParser = parser;
myLanguageLevel = level;
myConsumeAll = consumeAll;
}
public @NotNull JavaParserUtil.ParserWrapper getParser() {
return myParser;
}
public boolean consumeAll() {
return myConsumeAll;
}
public @NotNull LanguageLevel getLanguageLevel() {
return myLanguageLevel;
}
@Override
public TreeElement getFirstChildNode() {
try {
return super.getFirstChildNode();
}
catch (IncompleteFragmentParsingException e) {
myParserError = e;
return null; // masquerade parser errors
}
}
@Override
public TreeElement getLastChildNode() {
try {
return super.getLastChildNode();
}
catch (IncompleteFragmentParsingException e) {
myParserError = e;
return null; // masquerade parser errors
}
}
public @Nullable Throwable getParserError() {
return myParserError;
}
}
@@ -0,0 +1,157 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.psi.impl.source;
import com.intellij.lang.WhitespacesAndCommentsBinder;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.JavaFeature;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.ParentAwareTokenSet;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.intellij.psi.impl.source.BasicElementTypes.BASIC_JAVA_PLAIN_COMMENT_BIT_SET;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.BASIC_DOC_COMMENT;
import static com.intellij.psi.tree.ParentAwareTokenSet.create;
import static com.intellij.psi.tree.ParentAwareTokenSet.orSet;
@Deprecated
public class OldParserWhiteSpaceAndCommentSetHolder {
public static final OldParserWhiteSpaceAndCommentSetHolder INSTANCE = new OldParserWhiteSpaceAndCommentSetHolder();
private static final ParentAwareTokenSet PRECEDING_COMMENT_SET =
orSet(create(BasicJavaElementType.BASIC_MODULE, BasicJavaElementType.BASIC_IMPLICIT_CLASS),
BasicElementTypes.BASIC_FULL_MEMBER_BIT_SET);
private static final ParentAwareTokenSet TRAILING_COMMENT_SET =
orSet(create(BasicJavaElementType.BASIC_PACKAGE_STATEMENT), BasicElementTypes.BASIC_IMPORT_STATEMENT_BASE_BIT_SET,
BasicElementTypes.BASIC_FULL_MEMBER_BIT_SET, BasicElementTypes.BASIC_JAVA_STATEMENT_BIT_SET);
private OldParserWhiteSpaceAndCommentSetHolder() {
}
private final WhitespacesAndCommentsBinder PRECEDING_COMMENT_BINDER_WITH_MARKDOWN =
new PrecedingWhitespacesAndCommentsBinder(false, true);
private final WhitespacesAndCommentsBinder SPECIAL_PRECEDING_COMMENT_BINDER_WITH_MARKDOWN =
new PrecedingWhitespacesAndCommentsBinder(true, true);
private final WhitespacesAndCommentsBinder PRECEDING_COMMENT_BINDER_WITHOUT_MARKDOWN =
new PrecedingWhitespacesAndCommentsBinder(false, false);
private final WhitespacesAndCommentsBinder SPECIAL_PRECEDING_COMMENT_BINDER_WITHOUT_MARKDOWN =
new PrecedingWhitespacesAndCommentsBinder(true, false);
private final WhitespacesAndCommentsBinder TRAILING_COMMENT_BINDER = new TrailingWhitespacesAndCommentsBinder();
public WhitespacesAndCommentsBinder getPrecedingCommentBinder(@NotNull LanguageLevel myLanguageLevel) {
return JavaFeature.MARKDOWN_COMMENT.isSufficient(myLanguageLevel)
? PRECEDING_COMMENT_BINDER_WITH_MARKDOWN
: PRECEDING_COMMENT_BINDER_WITHOUT_MARKDOWN;
}
public WhitespacesAndCommentsBinder getSpecialPrecedingCommentBinder(@NotNull LanguageLevel myLanguageLevel) {
return JavaFeature.MARKDOWN_COMMENT.isSufficient(myLanguageLevel)
? SPECIAL_PRECEDING_COMMENT_BINDER_WITH_MARKDOWN
: SPECIAL_PRECEDING_COMMENT_BINDER_WITHOUT_MARKDOWN;
}
public WhitespacesAndCommentsBinder getTrailingCommentBinder() {
return TRAILING_COMMENT_BINDER;
}
public ParentAwareTokenSet getPrecedingCommentSet() {
return PRECEDING_COMMENT_SET;
}
public ParentAwareTokenSet getTrailingCommentSet() {
return TRAILING_COMMENT_SET;
}
private static class PrecedingWhitespacesAndCommentsBinder implements WhitespacesAndCommentsBinder {
private final boolean myAfterEmptyImport;
private final boolean mySupportMarkdown;
PrecedingWhitespacesAndCommentsBinder(final boolean afterImport, final boolean supportMarkdown) {
this.myAfterEmptyImport = afterImport;
this.mySupportMarkdown = supportMarkdown;
}
@Override
public int getEdgePosition(final List<? extends IElementType> tokens, final boolean atStreamEdge, final TokenTextGetter
getter) {
if (tokens.isEmpty()) return 0;
// 1. bind doc comment
// now there are markdown comments.
if (mySupportMarkdown) {
//collect everything
for (int idx = tokens.size() - 1; idx >= 0; idx--) {
if (BasicJavaAstTreeUtil.is(tokens.get(idx), BASIC_DOC_COMMENT)) return idx;
}
}
else {
// To preserve previous orders, let's try to find the first non-markdown comment (and skip markdown comments).
// If there is no non-markdown, take the first markdown
for (int idx = tokens.size() - 1; idx >= 0; idx--) {
if (BasicJavaAstTreeUtil.is(tokens.get(idx), BASIC_DOC_COMMENT) && !isDocMarkdownComment(idx, getter)) {
return idx;
}
}
for (int idx = tokens.size() - 1; idx >= 0; idx--) {
if (BasicJavaAstTreeUtil.is(tokens.get(idx), BASIC_DOC_COMMENT)) return idx;
}
}
// 2. bind plain comments
int result = tokens.size();
for (int idx = tokens.size() - 1; idx >= 0; idx--) {
final IElementType tokenType = tokens.get(idx);
if (TokenSet.WHITE_SPACE.contains(tokenType)) {
if (StringUtil.getLineBreakCount(getter.get(idx)) > 1) break;
}
else if (BASIC_JAVA_PLAIN_COMMENT_BIT_SET.contains(tokenType)) {
if (atStreamEdge ||
(idx == 0 && myAfterEmptyImport) ||
(idx > 0 && TokenSet.WHITE_SPACE.contains(tokens.get(idx - 1)) && StringUtil.containsLineBreak(getter.get(idx - 1)))) {
result = idx;
}
}
else {
break;
}
}
return result;
}
private static boolean isDocMarkdownComment(int idx, @NotNull TokenTextGetter getter) {
CharSequence sequence = getter.get(idx);
return sequence.length() >= 3 && "///".equals(sequence.subSequence(0, 3).toString());
}
}
private static class TrailingWhitespacesAndCommentsBinder implements WhitespacesAndCommentsBinder {
@Override
public int getEdgePosition(final List<? extends IElementType> tokens, final boolean atStreamEdge, final TokenTextGetter getter) {
if (tokens.isEmpty()) return 0;
int result = 0;
for (int idx = 0; idx < tokens.size(); idx++) {
final IElementType tokenType = tokens.get(idx);
if (TokenSet.WHITE_SPACE.contains(tokenType)) {
if (StringUtil.containsLineBreak(getter.get(idx))) break;
}
else if (BASIC_JAVA_PLAIN_COMMENT_BIT_SET.contains(tokenType)) {
result = idx + 1;
}
else {
break;
}
}
return result;
}
}
}
@@ -8,7 +8,6 @@ import com.intellij.lang.ASTNode;
import com.intellij.lang.LanguageASTFactory;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.lang.java.parser.BasicJavaParserUtil;
import com.intellij.lang.java.parser.JavaParser;
import com.intellij.lang.java.parser.JavaParserUtil;
import com.intellij.lang.java.syntax.JavaElementTypeConverterExtension;
@@ -70,7 +69,7 @@ public class OldJavaParsingTestConfigurator
}
@Override
public @Nullable SyntaxNode createFileSyntaxNode(@NotNull String text, BasicJavaParserUtil.@Nullable ParserWrapper parserWrapper) {
public @Nullable SyntaxNode createFileSyntaxNode(@NotNull String text, JavaParserUtil.@Nullable ParserWrapper parserWrapper) {
return null;
}