From 7e9f1978a29b1541ea711af780ddb88e299466ec Mon Sep 17 00:00:00 2001 From: Mikhail Pyltsin Date: Thu, 10 Oct 2024 14:06:10 +0200 Subject: [PATCH] [java-highlighting] IDEA-357214 'Module' is highlighted as an error when module name is expected GitOrigin-RevId: c7ca9de1821e79b96a05ab74a3cbad7052ceb827 --- .../daemon/impl/analysis/HighlightUtil.java | 14 +++++ .../messages/JavaPsiBundle.properties | 1 + .../lang/java/parser/BasicFileParser.java | 11 +++- .../AbstractBasicImportParsingTest.java | 1 + .../ImportModuleBrokenStatement.java | 7 +++ .../ImportModuleBrokenStatement.txt | 57 +++++++++++++++++++ .../ImportModuleBrokenStatement_node.txt | 57 +++++++++++++++++++ .../daemon/ModuleHighlightingTest.kt | 14 +++++ 8 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement.java create mode 100644 java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement.txt create mode 100644 java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement_node.txt diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index b67773689c30..9cef7af7c317 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -3537,6 +3537,20 @@ public final class HighlightUtil { return null; } + //do not highlight module keyword if the statement is not complete + //see com.intellij.lang.java.parser.BasicFileParser.parseImportStatement + if (PsiKeyword.MODULE.equals(ref.getText()) && refParent instanceof PsiImportStatement && + PsiUtil.isAvailable(JavaFeature.MODULE_IMPORT_DECLARATIONS, ref)) { + PsiElement importKeywordExpected = PsiTreeUtil.skipWhitespacesAndCommentsBackward(ref); + PsiElement errorElementExpected = PsiTreeUtil.skipWhitespacesAndCommentsForward(ref); + if (importKeywordExpected instanceof PsiKeyword keyword && + keyword.textMatches(PsiKeyword.IMPORT) && + errorElementExpected instanceof PsiErrorElement errorElement && + JavaPsiBundle.message("expected.identifier.or.semicolon").equals(errorElement.getErrorDescription())) { + return null; + } + } + JavaResolveResult[] results = ref.multiResolve(true); String description; if (results.length > 1) { diff --git a/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties b/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties index a7cf6d9d2a01..aa574d6c3ae0 100644 --- a/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties +++ b/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties @@ -161,6 +161,7 @@ expected.rbracket=']' expected expected.resource=Resource definition expected expected.rparen=')' expected expected.semicolon=';' expected +expected.identifier.or.semicolon=Identifier or ';' expected expected.statement=Statement expected expected.string=String literal expected expected.switch.label='case', 'default' or '}' expected diff --git a/java/java-frontback-psi-impl/src/com/intellij/lang/java/parser/BasicFileParser.java b/java/java-frontback-psi-impl/src/com/intellij/lang/java/parser/BasicFileParser.java index 1cf529430777..20d5d2d70302 100644 --- a/java/java-frontback-psi-impl/src/com/intellij/lang/java/parser/BasicFileParser.java +++ b/java/java-frontback-psi-impl/src/com/intellij/lang/java/parser/BasicFileParser.java @@ -193,6 +193,7 @@ public class BasicFileParser { 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; @@ -202,7 +203,15 @@ public class BasicFileParser { } else { isOk = myParser.getReferenceParser().parseImportCodeReference(builder, isStatic); } - if (isOk) semicolon(builder); + + //if it is `module` it should expect or `;` or `identifier` + if (isOk && !isModule && !isStatic && builder.getTokenType() != JavaTokenType.SEMICOLON && + PsiKeyword.MODULE.equals(identifierText)) { + BasicJavaParserUtil.error(builder, JavaPsiBundle.message("expected.identifier.or.semicolon")); + } + else if (isOk) { + semicolon(builder); + } done(statement, type, myWhiteSpaceAndCommentSetHolder); return statement; diff --git a/java/java-frontback-tests/testSrc/com/intellij/java/parser/AbstractBasicImportParsingTest.java b/java/java-frontback-tests/testSrc/com/intellij/java/parser/AbstractBasicImportParsingTest.java index d2527553037c..58410ddc21fe 100644 --- a/java/java-frontback-tests/testSrc/com/intellij/java/parser/AbstractBasicImportParsingTest.java +++ b/java/java-frontback-tests/testSrc/com/intellij/java/parser/AbstractBasicImportParsingTest.java @@ -18,4 +18,5 @@ public abstract class AbstractBasicImportParsingTest extends AbstractBasicJavaPa public void testModuleImport() { doTest(true); } public void testImportWithModulePackage() { doTest(true); } public void testImportWithModuleClass() { doTest(true); } + public void testImportModuleBrokenStatement() { doTest(true); } } \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement.java b/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement.java new file mode 100644 index 000000000000..b8cc6934edae --- /dev/null +++ b/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement.java @@ -0,0 +1,7 @@ +import module; +import module +import module +import module a; + +public class Test { +} diff --git a/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement.txt b/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement.txt new file mode 100644 index 000000000000..9c6a8e7dda98 --- /dev/null +++ b/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement.txt @@ -0,0 +1,57 @@ +PsiJavaFile:ImportModuleBrokenStatement.java + PsiImportList + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:module + PsiIdentifier:module('module') + PsiReferenceParameterList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n') + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:module + PsiIdentifier:module('module') + PsiReferenceParameterList + + PsiErrorElement:Identifier or ';' expected + + PsiWhiteSpace('\n') + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:module + PsiIdentifier:module('module') + PsiReferenceParameterList + + PsiErrorElement:Identifier or ';' expected + + PsiWhiteSpace('\n') + PsiImportModuleStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiKeyword:module('module') + PsiWhiteSpace(' ') + PsiJavaModuleReference + PsiIdentifier:a('a') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n\n') + PsiClass:Test + PsiModifierList:public + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:Test('Test') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace('\n') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement_node.txt b/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement_node.txt new file mode 100644 index 000000000000..b3309a33dad4 --- /dev/null +++ b/java/java-tests/testData/psi/parser-full/importParsing/ImportModuleBrokenStatement_node.txt @@ -0,0 +1,57 @@ +java.FILE + IMPORT_LIST + IMPORT_STATEMENT + IMPORT_KEYWORD + WHITE_SPACE + JAVA_CODE_REFERENCE + IDENTIFIER + REFERENCE_PARAMETER_LIST + + SEMICOLON + WHITE_SPACE + IMPORT_STATEMENT + IMPORT_KEYWORD + WHITE_SPACE + JAVA_CODE_REFERENCE + IDENTIFIER + REFERENCE_PARAMETER_LIST + + ERROR_ELEMENT + + WHITE_SPACE + IMPORT_STATEMENT + IMPORT_KEYWORD + WHITE_SPACE + JAVA_CODE_REFERENCE + IDENTIFIER + REFERENCE_PARAMETER_LIST + + ERROR_ELEMENT + + WHITE_SPACE + IMPORT_MODULE_STATEMENT + IMPORT_KEYWORD + WHITE_SPACE + MODULE + WHITE_SPACE + MODULE_REFERENCE + IDENTIFIER + SEMICOLON + WHITE_SPACE + CLASS + MODIFIER_LIST + PUBLIC_KEYWORD + WHITE_SPACE + CLASS_KEYWORD + WHITE_SPACE + IDENTIFIER + TYPE_PARAMETER_LIST + + EXTENDS_LIST + + IMPLEMENTS_LIST + + WHITE_SPACE + LBRACE + WHITE_SPACE + RBRACE \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/ModuleHighlightingTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/ModuleHighlightingTest.kt index 4497cc36d767..db2ad1c21176 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/ModuleHighlightingTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/ModuleHighlightingTest.kt @@ -27,6 +27,7 @@ import com.intellij.openapi.vfs.ex.temp.TempFileSystem import com.intellij.platform.backend.workspace.WorkspaceModel import com.intellij.platform.workspace.jps.entities.ModuleId import com.intellij.platform.workspace.jps.entities.modifyModuleEntity +import com.intellij.pom.java.JavaFeature import com.intellij.pom.java.LanguageLevel import com.intellij.psi.JavaCompilerConfigurationProxy import com.intellij.psi.PsiJavaModule @@ -765,6 +766,19 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { """.trimIndent()) } + fun testBrokenImportModuleStatement() { + IdeaTestUtil.withLevel(module, JavaFeature.MODULE_IMPORT_DECLARATIONS.minimumLevel){ + highlight("A.java", """ + package a; + + import module + + public class A { + } + """.trimIndent()) + } + } + private fun addVirtualManifest(moduleName: String, attributes: Map) { runWriteActionAndWait { WorkspaceModel.getInstance(myFixture.project).updateProjectModel { storage ->