mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java] IDEA-365317 integrate parsers
GitOrigin-RevId: 47daae1f9a9f54eed9d0e1485e10959e3447afd2
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7831b58155
commit
ed8e5dde12
+9
@@ -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();
|
||||
|
||||
+4
-4
@@ -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 {
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
|
||||
+4
-5
@@ -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;
|
||||
}
|
||||
}
|
||||
+157
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user