WIPPU
diff --git a/compiler/psi/src/org/jetbrains/kotlin/KtFileWithExpressions.kt b/compiler/psi/src/org/jetbrains/kotlin/KtFileWithExpressions.kt
new file mode 100644
index 0000000..549a8e2
--- /dev/null
+++ b/compiler/psi/src/org/jetbrains/kotlin/KtFileWithExpressions.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin
+
+import com.intellij.lang.ASTNode
+import com.intellij.psi.FileViewProvider
+import org.jetbrains.kotlin.psi.KtBlockExpression
+import org.jetbrains.kotlin.psi.KtDeclarationImpl
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtVisitor
+
+class KtFileWithExpressions(
+ viewProvider: FileViewProvider,
+) : KtFile(viewProvider, isCompiled = false) {
+ init {
+ contentElementType = KtNodeTypes.FILE_WITH_EXPRESSIONS
+ }
+
+ val expressionHolders: List<KtFileExpressionHolder>
+ get() = findChildrenByClass(KtFileExpressionHolder::class.java).toList()
+}
+
+class KtFileExpressionHolder(node: ASTNode) : KtDeclarationImpl(node) {
+ val block: KtBlockExpression?
+ get() = findChildByClass(KtBlockExpression::class.java)
+
+ val expressions: List<KtExpression>
+ get() = block?.statements.orEmpty()
+
+ override fun <R, D> accept(visitor: KtVisitor<R, D>, data: D): R? = visitor.visitKtFileExpressionHolder(this, data)
+}
\ No newline at end of file
diff --git a/compiler/psi/src/org/jetbrains/kotlin/KtNodeTypes.java b/compiler/psi/src/org/jetbrains/kotlin/KtNodeTypes.java
index 56ec02b..bb03c88 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/KtNodeTypes.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/KtNodeTypes.java
@@ -28,6 +28,8 @@
IElementType CLASS = KtStubElementTypes.CLASS;
IElementType FUN = KtStubElementTypes.FUNCTION;
IElementType PROPERTY = KtStubElementTypes.PROPERTY;
+ IElementType FILE_EXPRESSION_HOLDER = new KtNodeType("FILE_EXPRESSION_HOLDER", KtFileExpressionHolder.class);
+
IElementType DESTRUCTURING_DECLARATION = new KtNodeType("DESTRUCTURING_DECLARATION", KtDestructuringDeclaration.class);
IElementType DESTRUCTURING_DECLARATION_ENTRY = new KtNodeType("DESTRUCTURING_DECLARATION_ENTRY", KtDestructuringDeclarationEntry.class);
@@ -168,4 +170,6 @@
IFileElementType TYPE_CODE_FRAGMENT = KtStubElementTypes.TYPE_CODE_FRAGMENT;
IFileElementType EXPRESSION_CODE_FRAGMENT = KtStubElementTypes.EXPRESSION_CODE_FRAGMENT;
IFileElementType BLOCK_CODE_FRAGMENT = KtStubElementTypes.BLOCK_CODE_FRAGMENT;
+
+ IFileElementType FILE_WITH_EXPRESSIONS = KtStubElementTypes.FILE_WITH_EXPRESSIONS;
}
diff --git a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParser.java b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParser.java
index 9651885..f67b059 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParser.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParser.java
@@ -81,6 +81,13 @@
}
@NotNull
+ public static ASTNode parseFileWithExpressions(PsiBuilder psiBuilder) {
+ KotlinParsing ktParsing = KotlinParsing.createForTopLevel(new SemanticWhitespaceAwarePsiBuilderImpl(psiBuilder));
+ ktParsing.parseFileWithExpressions();
+ return psiBuilder.getTreeBuilt();
+ }
+
+ @NotNull
public static ASTNode parseLambdaExpression(PsiBuilder psiBuilder) {
KotlinParsing ktParsing = KotlinParsing.createForTopLevel(new SemanticWhitespaceAwarePsiBuilderImpl(psiBuilder));
ktParsing.parseLambdaExpression();
diff --git a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java
index 6d23221..9a77ba5 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java
@@ -29,6 +29,7 @@
import static org.jetbrains.kotlin.KtNodeTypes.*;
import static org.jetbrains.kotlin.lexer.KtTokens.*;
+import static org.jetbrains.kotlin.parsing.KotlinExpressionParsing.EXPRESSION_FIRST;
import static org.jetbrains.kotlin.parsing.KotlinParsing.AnnotationParsingMode.*;
import static org.jetbrains.kotlin.parsing.KotlinWhitespaceAndCommentsBindersKt.PRECEDING_ALL_BINDER;
import static org.jetbrains.kotlin.parsing.KotlinWhitespaceAndCommentsBindersKt.TRAILING_ALL_BINDER;
@@ -184,6 +185,19 @@
fileMarker.done(KT_FILE);
}
+ void parseFileWithExpressions() {
+ PsiBuilder.Marker fileMarker = mark();
+
+ parsePreamble();
+
+ while (!eof()) {
+ parseTopLevelDeclaration();
+ }
+
+ checkUnclosedBlockComment();
+ fileMarker.done(FILE_WITH_EXPRESSIONS);
+ }
+
private void checkUnclosedBlockComment() {
if (BLOCK_DOC_COMMENT_SET.contains(myBuilder.rawLookup(-1))) {
int startOffset = myBuilder.rawTokenTypeStart(-1);
@@ -485,7 +499,7 @@
* : object
* ;
*/
- private void parseTopLevelDeclaration() {
+ private void parseTopLevelDeclaration(boolean allowExpressions) {
if (at(SEMICOLON)) {
advance(); // SEMICOLON
return;
@@ -501,24 +515,36 @@
IElementType declType = parseCommonDeclaration(detector, NameParsingMode.REQUIRED, DeclarationParsingMode.MEMBER_OR_TOPLEVEL);
- if (declType == null && at(LBRACE)) {
- error("Expecting a top level declaration");
- parseBlock();
- declType = FUN;
- }
+ if (declType == null && allowExpressions && atSet(EXPRESSION_FIRST)) {
+ parseExpressionHolder();
+ } else {
+ if (declType == null && at(LBRACE)) {
+ error("Expecting a top level declaration");
+ parseBlock();
+ declType = FUN;
+ }
- if (declType == null && at(IMPORT_KEYWORD)) {
- error("imports are only allowed in the beginning of file");
- parseImportDirectives();
- decl.drop();
+ if (declType == null && at(IMPORT_KEYWORD)) {
+ error("imports are only allowed in the beginning of file");
+ parseImportDirectives();
+ decl.drop();
+ }
+ else if (declType == null) {
+ errorAndAdvance("Expecting a top level declaration");
+ decl.drop();
+ }
+ else {
+ closeDeclarationWithCommentBinders(decl, declType, true);
+ }
}
- else if (declType == null) {
- errorAndAdvance("Expecting a top level declaration");
- decl.drop();
- }
- else {
- closeDeclarationWithCommentBinders(decl, declType, true);
- }
+ }
+
+ private void parseExpressionHolder() {
+ PsiBuilder.Marker holder = mark();
+ PsiBuilder.Marker block = mark();
+ myExpressionParsing.parseExpression();
+ block.done(BLOCK);
+ holder.done(FILE_EXPRESSION_HOLDER);
}
public IElementType parseCommonDeclaration(
@@ -2347,7 +2373,7 @@
recoverOnParenthesizedWordForPlatformTypes(0, "Mutable", true);
if (expect(IDENTIFIER, "Expecting type name",
- TokenSet.orSet(KotlinExpressionParsing.EXPRESSION_FIRST, KotlinExpressionParsing.EXPRESSION_FOLLOW, DECLARATION_FIRST))) {
+ TokenSet.orSet(EXPRESSION_FIRST, KotlinExpressionParsing.EXPRESSION_FOLLOW, DECLARATION_FIRST))) {
reference.done(REFERENCE_EXPRESSION);
}
else {
diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtFileWithExpressionsType.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/KtFileWithExpressionsType.kt
new file mode 100644
index 0000000..a6dec68
--- /dev/null
+++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtFileWithExpressionsType.kt
@@ -0,0 +1,22 @@
+package org.jetbrains.kotlin.psi
+
+import com.intellij.lang.ASTNode
+import com.intellij.lang.PsiBuilderFactory
+import com.intellij.psi.PsiElement
+import com.intellij.psi.tree.IFileElementType
+import org.jetbrains.kotlin.idea.KotlinLanguage
+import org.jetbrains.kotlin.parsing.KotlinParser
+
+class KtFileWithExpressionsType : IFileElementType(NAME, KotlinLanguage.INSTANCE) {
+ override fun doParseContents(chameleon: ASTNode, psi: PsiElement): ASTNode? {
+ val project = psi.project
+ val languageForParser = getLanguageForParser(psi)
+ val builder = PsiBuilderFactory.getInstance().createBuilder(project, chameleon, null, languageForParser, chameleon.chars)
+ return KotlinParser.parseFileWithExpressions(builder).firstChildNode
+ }
+
+ companion object {
+ private const val NAME = "kotlin.FILE_WITH_EXPRESSIONS"
+ }
+}
+
diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
index 5d33cf7..b663c27 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
+++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
@@ -11,9 +11,13 @@
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
+import com.intellij.psi.PsiManager
+import com.intellij.psi.impl.PsiManagerEx
import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.testFramework.LightVirtualFile
import com.intellij.util.LocalTimeCounter
import org.jetbrains.annotations.NonNls
+import org.jetbrains.kotlin.KtFileWithExpressions
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
@@ -607,6 +611,16 @@
return KtBlockCodeFragment(project, "fragment.kt", text, null, context)
}
+ fun createFileWithExpressions(fileName: String, @NonNls text: String): KtFileWithExpressions {
+ val psiManager = PsiManager.getInstance(project) as PsiManagerEx
+ val viewProvider = psiManager.fileManager.createFileViewProvider(
+ LightVirtualFile(fileName, KotlinFileType.INSTANCE, text),
+ /* eventSystemEnabled = */true
+ )
+ return KtFileWithExpressions(viewProvider)
+ }
+
+
fun createIf(condition: KtExpression, thenExpr: KtExpression, elseExpr: KtExpression? = null): KtIfExpression {
return (if (elseExpr != null)
createExpressionByPattern("if ($0) $1 else $2", condition, thenExpr, elseExpr) as KtIfExpression
diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtVisitor.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtVisitor.java
index 0894e0b..0e7505e 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtVisitor.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtVisitor.java
@@ -18,6 +18,7 @@
import com.intellij.psi.PsiElementVisitor;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.kotlin.KtFileExpressionHolder;
public class KtVisitor<R, D> extends PsiElementVisitor {
public R visitKtElement(@NotNull KtElement element, D data) {
@@ -364,6 +365,10 @@
return visitAnonymousInitializer(initializer, data);
}
+ public R visitKtFileExpressionHolder(@NotNull KtFileExpressionHolder holder, D data) {
+ return visitDeclaration(holder, data);
+ }
+
public R visitClassInitializer(@NotNull KtClassInitializer initializer, D data) {
return visitAnonymousInitializer(initializer, data);
}
diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtStubElementTypes.java b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtStubElementTypes.java
index 8a80754..04cf406 100644
--- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtStubElementTypes.java
+++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtStubElementTypes.java
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.psi.stubs.elements;
import com.intellij.psi.tree.TokenSet;
+import org.jetbrains.kotlin.KtFileExpressionHolder;
import org.jetbrains.kotlin.psi.*;
public interface KtStubElementTypes {
@@ -93,6 +94,7 @@
KtTypeCodeFragmentType TYPE_CODE_FRAGMENT = new KtTypeCodeFragmentType();
KtExpressionCodeFragmentType EXPRESSION_CODE_FRAGMENT = new KtExpressionCodeFragmentType();
KtBlockCodeFragmentType BLOCK_CODE_FRAGMENT = new KtBlockCodeFragmentType();
+ KtFileWithExpressionsType FILE_WITH_EXPRESSIONS = new KtFileWithExpressionsType();
KtTypeProjectionElementType TYPE_PROJECTION = new KtTypeProjectionElementType("TYPE_PROJECTION");
diff --git a/compiler/testData/parseFileWithExpressions/topLevelExpression.kt b/compiler/testData/parseFileWithExpressions/topLevelExpression.kt
new file mode 100644
index 0000000..c294f55
--- /dev/null
+++ b/compiler/testData/parseFileWithExpressions/topLevelExpression.kt
@@ -0,0 +1,15 @@
+package a.b.c
+
+class A
+val x = 10
+
+1 + 2
+
+run {
+
+}
+
+println(42)
+
+val y
+ get() = 1
\ No newline at end of file
diff --git a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt
index cde1258..c94c58f 100644
--- a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt
+++ b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt
@@ -93,6 +93,7 @@
model("psi", testMethod = "doParsingTest", pattern = "^(.*)\\.kts?$")
model("parseCodeFragment/expression", testMethod = "doExpressionCodeFragmentParsingTest", extension = "kt")
model("parseCodeFragment/block", testMethod = "doBlockCodeFragmentParsingTest", extension = "kt")
+ model("parseFileWithExpressions", testMethod = "doFileWithExpressionsTest", extension = "kt")
}
testClass<AbstractLightAnalysisModeTest> {
diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java b/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java
index cdcf231..31f3546 100644
--- a/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java
+++ b/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java
@@ -91,6 +91,10 @@
doBaseTest(filePath, KtNodeTypes.EXPRESSION_CODE_FRAGMENT, null);
}
+ protected void doFileWithExpressionsTest(@NotNull String filePath) {
+ doBaseTest(filePath, KtNodeTypes.FILE_WITH_EXPRESSIONS, null);
+ }
+
protected void doBlockCodeFragmentParsingTest(@NotNull String filePath) {
doBaseTest(filePath, KtNodeTypes.BLOCK_CODE_FRAGMENT, null);
}
@@ -137,6 +141,8 @@
}
else if (fileType == KtNodeTypes.BLOCK_CODE_FRAGMENT) {
return psiFactory.createBlockCodeFragment(fileContent, null);
+ } else if (fileType == KtNodeTypes.FILE_WITH_EXPRESSIONS) {
+ return psiFactory.createFileWithExpressions(FileUtil.getNameWithoutExtension(PathUtil.getFileName(filePath)), fileContent);
}
else {
return createPsiFile(FileUtil.getNameWithoutExtension(PathUtil.getFileName(filePath)), fileContent);