diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt index bca9f64a521..cd2c936fea4 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt @@ -106,6 +106,8 @@ enum class PositioningStrategy(private val strategy: String? = null) { OPERATOR_MODIFIER, NON_FINAL_MODIFIER_OR_NAME, ENUM_MODIFIER, + FIELD_KEYWORD, + ; val expressionToCreate get() = "SourceElementPositioningStrategies.${strategy ?: name}" diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt index 0daf45db934..3621fc6ce39 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt @@ -379,6 +379,25 @@ object LightTreePositioningStrategies { val ENUM_MODIFIER: LightTreePositioningStrategy = ModifierSetBasedLightTreePositioningStrategy(TokenSet.create(KtTokens.ENUM_KEYWORD)) + val FIELD_KEYWORD: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + override fun mark( + node: LighterASTNode, + startOffset: Int, + endOffset: Int, + tree: FlyweightCapableTreeStructure + ): List { + val fieldKeyword = tree.fieldKeyword(node) + if (fieldKeyword != null) { + return markElement(fieldKeyword, startOffset, endOffset, tree, node) + } + return DEFAULT.mark(node, startOffset, endOffset, tree) + } + + override fun isValid(node: LighterASTNode, tree: FlyweightCapableTreeStructure): Boolean { + return tree.fieldKeyword(node) != null + } + } + val INLINE_PARAMETER_MODIFIER: LightTreePositioningStrategy = ModifierSetBasedLightTreePositioningStrategy(TokenSet.create(KtTokens.NOINLINE_KEYWORD, KtTokens.CROSSINLINE_KEYWORD)) @@ -992,6 +1011,9 @@ private fun FlyweightCapableTreeStructure.elseKeyword(node: Ligh private fun FlyweightCapableTreeStructure.returnKeyword(node: LighterASTNode): LighterASTNode? = findChildByType(node, KtTokens.RETURN_KEYWORD) +private fun FlyweightCapableTreeStructure.fieldKeyword(node: LighterASTNode): LighterASTNode? = + findChildByType(node, KtTokens.FIELD_KEYWORD) + internal fun FlyweightCapableTreeStructure.nameIdentifier(node: LighterASTNode): LighterASTNode? = findChildByType(node, KtTokens.IDENTIFIER) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt index 06ab709cb93..c9530b1ba8d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt @@ -319,4 +319,9 @@ object SourceElementPositioningStrategies { LightTreePositioningStrategies.ENUM_MODIFIER, PositioningStrategies.ENUM_MODIFIER ) + + val FIELD_KEYWORD = SourceElementPositioningStrategy( + LightTreePositioningStrategies.FIELD_KEYWORD, + PositioningStrategies.FIELD_KEYWORD + ) } diff --git a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 407741ba09b..82391456af4 100644 --- a/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend.common-psi/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -292,6 +292,13 @@ object PositioningStrategies { @JvmField val ENUM_MODIFIER: PositioningStrategy = modifierSetPosition(KtTokens.ENUM_KEYWORD) + @JvmField + val FIELD_KEYWORD: PositioningStrategy = object : DeclarationHeader() { + override fun mark(element: KtBackingField): List { + return markElement(element.fieldKeyword) + } + } + @JvmField val FOR_REDECLARATION: PositioningStrategy = object : PositioningStrategy() { override fun mark(element: PsiElement): List { diff --git a/compiler/psi/src/org/jetbrains/kotlin/KtNodeTypes.java b/compiler/psi/src/org/jetbrains/kotlin/KtNodeTypes.java index c32c0b8c7ab..99dcf744247 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/KtNodeTypes.java +++ b/compiler/psi/src/org/jetbrains/kotlin/KtNodeTypes.java @@ -79,6 +79,7 @@ public interface KtNodeTypes { IElementType TYPE_PROJECTION = KtStubElementTypes.TYPE_PROJECTION; IElementType PROPERTY_ACCESSOR = KtStubElementTypes.PROPERTY_ACCESSOR; + IElementType BACKING_FIELD = KtStubElementTypes.BACKING_FIELD; IElementType INITIALIZER_LIST = KtStubElementTypes.INITIALIZER_LIST; IElementType TYPE_CONSTRAINT_LIST = KtStubElementTypes.TYPE_CONSTRAINT_LIST; IElementType TYPE_CONSTRAINT = KtStubElementTypes.TYPE_CONSTRAINT; diff --git a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java index f0f95238887..89c262d4e7b 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java +++ b/compiler/psi/src/org/jetbrains/kotlin/parsing/KotlinParsing.java @@ -1415,9 +1415,12 @@ public class KotlinParsing extends AbstractKotlinParsing { myBuilder.restoreNewlinesState(); if (!hasNewLineWithSemicolon) { - AccessorKind accessorKind = parsePropertyGetterOrSetter(null); - if (accessorKind != null) { - parsePropertyGetterOrSetter(accessorKind); + PropertyComponentKind.Collector alreadyRead = new PropertyComponentKind.Collector(); + PropertyComponentKind propertyComponentKind = parsePropertyComponent(alreadyRead); + + while (propertyComponentKind != null) { + alreadyRead.collect(propertyComponentKind); + propertyComponentKind = parsePropertyComponent(alreadyRead); } if (!atSet(EOL_OR_SEMICOLON, RBRACE)) { @@ -1504,78 +1507,102 @@ public class KotlinParsing extends AbstractKotlinParsing { myBuilder.restoreNewlinesState(); } - private enum AccessorKind { GET, SET} + private enum PropertyComponentKind { + GET, + SET, + FIELD; + + static class Collector { + private final boolean[] collected = { false, false, false }; + + public void collect(PropertyComponentKind kind) { + collected[kind.ordinal()] = true; + } + + public boolean contains(PropertyComponentKind kind) { + return collected[kind.ordinal()]; + } + } + } /* - * getterOrSetter + * propertyComponent * : modifiers ("get" | "set") * : * ( "get" "(" ")" * | * "set" "(" modifiers parameter ")" + * | + * "field" * ) functionBody * ; */ @Nullable - private AccessorKind parsePropertyGetterOrSetter(@Nullable AccessorKind notAllowedKind) { - PsiBuilder.Marker getterOrSetter = mark(); + private PropertyComponentKind parsePropertyComponent(PropertyComponentKind.Collector notAllowedKind) { + PsiBuilder.Marker propertyComponent = mark(); parseModifierList(DEFAULT, TokenSet.EMPTY); - AccessorKind accessorKind; + PropertyComponentKind propertyComponentKind; if (at(GET_KEYWORD)) { - accessorKind = AccessorKind.GET; + propertyComponentKind = PropertyComponentKind.GET; } else if (at(SET_KEYWORD)) { - accessorKind = AccessorKind.SET; + propertyComponentKind = PropertyComponentKind.SET; + } + else if (at(FIELD_KEYWORD)) { + propertyComponentKind = PropertyComponentKind.FIELD; } else { - getterOrSetter.rollbackTo(); + propertyComponent.rollbackTo(); return null; } - if (accessorKind == notAllowedKind) { - getterOrSetter.rollbackTo(); + if (notAllowedKind.contains(propertyComponentKind)) { + propertyComponent.rollbackTo(); return null; } - advance(); // GET_KEYWORD or SET_KEYWORD + advance(); // GET_KEYWORD, SET_KEYWORD or FIELD_KEYWORD - if (!at(LPAR)) { + if (!at(LPAR) && propertyComponentKind != PropertyComponentKind.FIELD) { // Account for Jet-114 (val a : int get {...}) - TokenSet ACCESSOR_FIRST_OR_PROPERTY_END = TokenSet.orSet(MODIFIER_KEYWORDS, TokenSet.create(AT, GET_KEYWORD, SET_KEYWORD, EOL_OR_SEMICOLON, RBRACE)); + TokenSet ACCESSOR_FIRST_OR_PROPERTY_END = TokenSet.orSet(MODIFIER_KEYWORDS, TokenSet.create(AT, GET_KEYWORD, SET_KEYWORD, FIELD_KEYWORD, EOL_OR_SEMICOLON, RBRACE)); if (!atSet(ACCESSOR_FIRST_OR_PROPERTY_END)) { errorUntil("Accessor body expected", TokenSet.orSet(ACCESSOR_FIRST_OR_PROPERTY_END, TokenSet.create(LBRACE, LPAR, EQ))); } else { - closeDeclarationWithCommentBinders(getterOrSetter, PROPERTY_ACCESSOR, true); - return accessorKind; + closeDeclarationWithCommentBinders(propertyComponent, PROPERTY_ACCESSOR, true); + return propertyComponentKind; } } myBuilder.disableNewlines(); - expect(LPAR, "Expecting '('", TokenSet.create(RPAR, IDENTIFIER, COLON, LBRACE, EQ)); - if (accessorKind == AccessorKind.SET) { - PsiBuilder.Marker parameterList = mark(); - PsiBuilder.Marker setterParameter = mark(); - parseModifierList(DEFAULT, TokenSet.create(COMMA, COLON, RPAR)); - expect(IDENTIFIER, "Expecting parameter name", TokenSet.create(RPAR, COLON, LBRACE, EQ)); - if (at(COLON)) { - advance(); // COLON - parseTypeRef(); + if (propertyComponentKind != PropertyComponentKind.FIELD) { + expect(LPAR, "Expecting '('", TokenSet.create(RPAR, IDENTIFIER, COLON, LBRACE, EQ)); + if (propertyComponentKind == PropertyComponentKind.SET) { + PsiBuilder.Marker parameterList = mark(); + PsiBuilder.Marker setterParameter = mark(); + parseModifierList(DEFAULT, TokenSet.create(COMMA, COLON, RPAR)); + expect(IDENTIFIER, "Expecting parameter name", TokenSet.create(RPAR, COLON, LBRACE, EQ)); + + if (at(COLON)) { + advance(); // COLON + parseTypeRef(); + } + setterParameter.done(VALUE_PARAMETER); + if (at(COMMA)) { + advance(); // COMMA + } + parameterList.done(VALUE_PARAMETER_LIST); } - setterParameter.done(VALUE_PARAMETER); - if (at(COMMA)) { - advance(); // COMMA + if (!at(RPAR)) { + errorUntil("Expecting ')'", TokenSet.create(RPAR, COLON, LBRACE, RBRACE, EQ, EOL_OR_SEMICOLON)); + } + if (at(RPAR)) { + advance(); } - parameterList.done(VALUE_PARAMETER_LIST); - } - if (!at(RPAR)) { - errorUntil("Expecting ')'", TokenSet.create(RPAR, COLON, LBRACE, RBRACE, EQ, EOL_OR_SEMICOLON)); - } - if (at(RPAR)) { - advance(); } myBuilder.restoreNewlinesState(); @@ -1585,13 +1612,22 @@ public class KotlinParsing extends AbstractKotlinParsing { parseTypeRef(); } - parseFunctionContract(); + if (propertyComponentKind != PropertyComponentKind.FIELD) { + parseFunctionContract(); + parseFunctionBody(); + } else if (at(EQ)) { + advance(); + myExpressionParsing.parseExpression(); + consumeIf(SEMICOLON); + } - parseFunctionBody(); + if (propertyComponentKind == PropertyComponentKind.FIELD) { + closeDeclarationWithCommentBinders(propertyComponent, BACKING_FIELD, true); + } else { + closeDeclarationWithCommentBinders(propertyComponent, PROPERTY_ACCESSOR, true); + } - closeDeclarationWithCommentBinders(getterOrSetter, PROPERTY_ACCESSOR, true); - - return accessorKind; + return propertyComponentKind; } @NotNull diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtBackingField.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtBackingField.java new file mode 100644 index 00000000000..5ca3da2e18e --- /dev/null +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtBackingField.java @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.psi; + +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.lexer.KtTokens; +import org.jetbrains.kotlin.psi.stubs.KotlinBackingFieldStub; +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes; + +public class KtBackingField extends KtDeclarationStub + implements KtModifierListOwner, KtDeclarationWithInitializer { + public KtBackingField(@NotNull ASTNode node) { + super(node); + } + + public KtBackingField(@NotNull KotlinBackingFieldStub stub) { + super(stub, KtStubElementTypes.BACKING_FIELD); + } + + @Override + public R accept(@NotNull KtVisitor visitor, D data) { + return visitor.visitBackingField(this, data); + } + + @Nullable + public PsiElement getEqualsToken() { + return findChildByType(KtTokens.EQ); + } + + @Nullable + public KtTypeReference getReturnTypeReference() { + return getStubOrPsiChild(KtStubElementTypes.TYPE_REFERENCE); + } + + @NotNull + public PsiElement getNamePlaceholder() { + PsiElement it = getFieldKeyword(); + if (it != null) { + return it; + } + return getNode().getPsi(); + } + + @Nullable + @Override + public KtExpression getInitializer() { + return PsiTreeUtil.getNextSiblingOfType(getEqualsToken(), KtExpression.class); + } + + @Override + public boolean hasInitializer() { + return getInitializer() != null; + } + + @Override + public int getTextOffset() { + return getNamePlaceholder().getTextRange().getStartOffset(); + } + + public PsiElement getFieldKeyword() { + return findChildByType(KtTokens.FIELD_KEYWORD); + } +} diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtProperty.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtProperty.java index c7a728d199f..b710e4612bd 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtProperty.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtProperty.java @@ -191,6 +191,15 @@ public class KtProperty extends KtTypeParameterListOwnerStub return null; } + @Nullable + public KtBackingField getFieldDeclaration() { + for (KtBackingField field : getStubOrPsiChildrenAsList(KtStubElementTypes.BACKING_FIELD)) { + return field; + } + + return null; + } + public boolean hasDelegate() { KotlinPropertyStub stub = getStub(); if (stub != null) { diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/KtVisitor.java b/compiler/psi/src/org/jetbrains/kotlin/psi/KtVisitor.java index d4dc80f7655..ad94a11ecb3 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/KtVisitor.java +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/KtVisitor.java @@ -358,6 +358,10 @@ public class KtVisitor extends PsiElementVisitor { return visitDeclaration(accessor, data); } + public R visitBackingField(@NotNull KtBackingField accessor, D data) { + return visitDeclaration(accessor, data); + } + public R visitTypeConstraintList(@NotNull KtTypeConstraintList list, D data) { return visitKtElement(list, data); } diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/StubInterfaces.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/StubInterfaces.kt index 03f7b4d1596..f9dfe7d7681 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/StubInterfaces.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/StubInterfaces.kt @@ -121,6 +121,8 @@ interface KotlinPropertyAccessorStub : StubElement { fun hasBlockBody(): Boolean } +interface KotlinBackingFieldStub : StubElement + interface KotlinPropertyStub : KotlinCallableStubBase { fun isVar(): Boolean fun hasDelegate(): Boolean diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtBackingFieldElementType.java b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtBackingFieldElementType.java new file mode 100644 index 00000000000..8c274d0f4e7 --- /dev/null +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/elements/KtBackingFieldElementType.java @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.psi.stubs.elements; + +import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.stubs.StubInputStream; +import com.intellij.psi.stubs.StubOutputStream; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.psi.KtBackingField; +import org.jetbrains.kotlin.psi.stubs.KotlinBackingFieldStub; +import org.jetbrains.kotlin.psi.stubs.impl.KotlinBackingFieldStubImpl; + +import java.io.IOException; + +public class KtBackingFieldElementType extends KtStubElementType { + public KtBackingFieldElementType(@NotNull @NonNls String debugName) { + super(debugName, KtBackingField.class, KotlinBackingFieldStub.class); + } + + @Override + public KotlinBackingFieldStub createStub(@NotNull KtBackingField psi, StubElement parentStub) { + return new KotlinBackingFieldStubImpl(parentStub); + } + + @Override + public void serialize(@NotNull KotlinBackingFieldStub stub, @NotNull StubOutputStream dataStream) throws IOException {} + + @NotNull + @Override + public KotlinBackingFieldStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { + return new KotlinBackingFieldStubImpl(parentStub); + } +} 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 abc1b65f07e..076a1089585 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 @@ -26,6 +26,7 @@ public interface KtStubElementTypes { KtFunctionElementType FUNCTION = new KtFunctionElementType("FUN"); KtPropertyElementType PROPERTY = new KtPropertyElementType("PROPERTY"); KtPropertyAccessorElementType PROPERTY_ACCESSOR = new KtPropertyAccessorElementType("PROPERTY_ACCESSOR"); + KtBackingFieldElementType BACKING_FIELD = new KtBackingFieldElementType("BACKING_FIELD"); KtTypeAliasElementType TYPEALIAS = new KtTypeAliasElementType("TYPEALIAS"); KtClassElementType ENUM_ENTRY = new KtClassElementType("ENUM_ENTRY"); diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/impl/KotlinBackingFieldStubImpl.java b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/impl/KotlinBackingFieldStubImpl.java new file mode 100644 index 00000000000..75e5508c326 --- /dev/null +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/stubs/impl/KotlinBackingFieldStubImpl.java @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.psi.stubs.impl; + +import com.intellij.psi.stubs.StubElement; +import org.jetbrains.kotlin.psi.KtBackingField; +import org.jetbrains.kotlin.psi.stubs.KotlinBackingFieldStub; +import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes; + +public class KotlinBackingFieldStubImpl extends KotlinStubBaseImpl + implements KotlinBackingFieldStub { + public KotlinBackingFieldStubImpl(StubElement parent) { + super(parent, KtStubElementTypes.PROPERTY_ACCESSOR); + } +}