diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/FileParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/FileParser.java index f007e3c93248..feed7c31a520 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/FileParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/FileParser.java @@ -147,37 +147,41 @@ public class FileParser { private Pair parseImportList(PsiBuilder builder, Predicate stopper) { PsiBuilder.Marker list = builder.mark(); - IElementType tokenType = builder.getTokenType(); - boolean isEmpty = tokenType != JavaTokenType.IMPORT_KEYWORD && tokenType != JavaTokenType.SEMICOLON; - if (!isEmpty) { - PsiBuilder.Marker invalidElements = null; - while (!builder.eof()) { - if (stopper.test(builder)) { - break; - } - else if (builder.getTokenType() == JavaTokenType.SEMICOLON) { - builder.advanceLexer(); - continue; - } - - final PsiBuilder.Marker statement = parseImportStatement(builder); - if (statement != null) { - if (invalidElements != null) { - invalidElements.errorBefore(JavaErrorMessages.message("unexpected.token"), statement); - invalidElements = null; - } - continue; - } - - if (invalidElements == null) { - invalidElements = 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; } - if (invalidElements != null) { - invalidElements.error(JavaErrorMessages.message("unexpected.token")); + final PsiBuilder.Marker statement = parseImportStatement(builder); + if (statement != null) { + isEmpty = false; + if (invalidElements != null) { + invalidElements.errorBefore(JavaErrorMessages.message("unexpected.token"), statement); + invalidElements = null; + } + continue; } + + if (invalidElements == null) { + invalidElements = builder.mark(); + } + builder.advanceLexer(); + } + + if (invalidElements != null) { + invalidElements.error(JavaErrorMessages.message("unexpected.token")); + } + + if (isEmpty) { + PsiBuilder.Marker precede = list.precede(); + list.rollbackTo(); + list = precede; } done(list, JavaElementType.IMPORT_LIST); diff --git a/java/java-tests/testData/psi/parser-partial/files/ImportBrokenFromBeginning.txt b/java/java-tests/testData/psi/parser-partial/files/ImportBrokenFromBeginning.txt new file mode 100644 index 000000000000..a04bd0713845 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/ImportBrokenFromBeginning.txt @@ -0,0 +1,13 @@ +PsiJavaFile:ImportBrokenFromBeginning.java + PsiImportList + PsiErrorElement:Unexpected token + PsiIdentifier:xx('xx') + PsiWhiteSpace(' ') + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:a + PsiIdentifier:a('a') + PsiReferenceParameterList + + PsiJavaToken:SEMICOLON(';') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/parser/partial/FileParserTest.java b/java/java-tests/testSrc/com/intellij/java/parser/partial/FileParserTest.java index a377fd7f398f..e41039b60721 100644 --- a/java/java-tests/testSrc/com/intellij/java/parser/partial/FileParserTest.java +++ b/java/java-tests/testSrc/com/intellij/java/parser/partial/FileParserTest.java @@ -38,6 +38,7 @@ public class FileParserTest extends JavaParsingTestCase { public void testUnclosedImport1() { doParserTest("import java.awt.*"); } public void testUnclosedImport2() { doParserTest("import java.awt."); } public void testUnclosedImport3() { doParserTest("import static a"); } + public void testImportBrokenFromBeginning() { doParserTest("xx import a;"); } public void testExtraSemicolons() { doParserTest( diff --git a/java/java-tests/testSrc/com/intellij/java/psi/JavaStubsTest.groovy b/java/java-tests/testSrc/com/intellij/java/psi/JavaStubsTest.groovy index a07ad53dfd5e..7c0ea0b037ee 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/JavaStubsTest.groovy +++ b/java/java-tests/testSrc/com/intellij/java/psi/JavaStubsTest.groovy @@ -19,15 +19,21 @@ import com.intellij.codeInsight.AnnotationTargetUtil import com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ReadAction +import com.intellij.openapi.command.WriteCommandAction import com.intellij.psi.PsiAnnotationMethod import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiJavaFile import com.intellij.psi.PsiReferenceExpression +import com.intellij.psi.SyntaxTraverser import com.intellij.psi.impl.source.PsiClassImpl import com.intellij.psi.impl.source.PsiFileImpl +import com.intellij.psi.impl.source.tree.TreeUtil import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.searches.AnnotatedElementsSearch import com.intellij.psi.search.searches.ClassInheritorsSearch import com.intellij.psi.util.PsiUtil +import com.intellij.testFramework.PsiTestUtil import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import java.util.concurrent.Callable @@ -173,4 +179,29 @@ import java.lang.annotation.*; assert !((PsiFileImpl) cls.containingFile).contentsLoaded } + void "test breaking and adding import does not cause stub AST mismatch"() { + def file = myFixture.addFileToProject("a.java", "import foo.*; import bar.*; class Foo {}") as PsiJavaFile + def another = myFixture.addClass("package zoo; public class Another {}") + WriteCommandAction.runWriteCommandAction(project) { + file.viewProvider.document.insertString(file.text.indexOf('import'), 'x') + PsiDocumentManager.getInstance(project).commitAllDocuments() + file.importClass(another) + } + PsiTestUtil.checkStubsMatchText(file) + } + + void "test adding type before method call does not cause stub AST mismatch"() { + def file = myFixture.addFileToProject("a.java", """ +class Foo { + void foo() { + something(); + call(); + } +} +""") as PsiJavaFile + WriteCommandAction.runWriteCommandAction(project) { + file.viewProvider.document.insertString(file.text.indexOf('call'), 'char ') + PsiTestUtil.checkStubsMatchText(file) + } + } } \ No newline at end of file