diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 936053c07c8..e734946ca98 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1068,8 +1068,8 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("actualType") } - val UNDERSCORE_IS_RESERVED by error(PositioningStrategy.RESERVED_UNDERSCORE) - val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error(PositioningStrategy.RESERVED_UNDERSCORE) + val UNDERSCORE_IS_RESERVED by error(PositioningStrategy.RESERVED_UNDERSCORE) + val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error(PositioningStrategy.RESERVED_UNDERSCORE) val EQUALITY_NOT_APPLICABLE by error { parameter("operator") diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 259cc169957..66c589d114f 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -554,8 +554,8 @@ object FirErrors { val DELEGATE_SPECIAL_FUNCTION_AMBIGUITY by error2>>() val DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE by error2>>() val DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH by error3() - val UNDERSCORE_IS_RESERVED by error0(SourceElementPositioningStrategies.RESERVED_UNDERSCORE) - val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0(SourceElementPositioningStrategies.RESERVED_UNDERSCORE) + val UNDERSCORE_IS_RESERVED by error0(SourceElementPositioningStrategies.RESERVED_UNDERSCORE) + val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0(SourceElementPositioningStrategies.RESERVED_UNDERSCORE) val EQUALITY_NOT_APPLICABLE by error3() val EQUALITY_NOT_APPLICABLE_WARNING by warning3() val INCOMPATIBLE_ENUM_COMPARISON_ERROR by error2() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt index babce44adcc..65fa4ffdf84 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt @@ -5,108 +5,34 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression -import com.intellij.lang.LighterASTNode -import com.intellij.psi.PsiElement import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.impl.source.tree.LeafPsiElement -import org.jetbrains.kotlin.KtNodeTypes -import org.jetbrains.kotlin.fir.FirLightSourceElement -import org.jetbrains.kotlin.fir.FirPsiSourceElement -import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker import org.jetbrains.kotlin.fir.analysis.checkers.getChildren import org.jetbrains.kotlin.fir.analysis.diagnostics.* import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.diagnostics.ConeUnderscoreUsageWithoutBackticks import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.text -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType -import org.jetbrains.kotlin.psi.stubs.elements.KtDotQualifiedExpressionElementType +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtTypeProjection import org.jetbrains.kotlin.psi.stubs.elements.KtNameReferenceExpressionElementType +import org.jetbrains.kotlin.psi.stubs.elements.KtTypeProjectionElementType object FirReservedUnderscoreExpressionChecker : FirBasicExpressionChecker() { override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { - val source = expression.source - - if (expression is FirFunctionCall) { - val calleeReferenceSource = expression.calleeReference.source - if (calleeReferenceSource is FirLightSourceElement && calleeReferenceSource.lighterASTNode.tokenType == KtNodeTypes.OPERATION_REFERENCE) { - return + when (expression) { + is FirResolvable -> { + reportIfUnderscore(expression.calleeReference.source, context, reporter, isExpression = true) } - - reportIfUnderscore( - expression.calleeReference.source.text, expression.calleeReference.source, context, reporter, - isExpression = true - ) - } else if (expression is FirQualifiedAccess) { - if (source is FirPsiSourceElement) { - reportIfUnderscoreInQualifiedAccess(source, expression, context, reporter) - } else if (source is FirLightSourceElement) { - reportIfUnderscoreInQualifiedAccess(source, expression, context, reporter) - } - } else if (expression is FirGetClassCall) { - for (argument in expression.argumentList.arguments) { - reportIfUnderscore(argument.source.text, expression.source, context, reporter, isExpression = true) - } - } else if (expression is FirReturnExpression) { - var labelName: String? = null - if (source is FirPsiSourceElement) { - labelName = (source.psi.parent as? KtLabeledExpression)?.getLabelName() - } else if (source is FirLightSourceElement) { - val parent = source.treeStructure.getParent(source.lighterASTNode) - if (parent != null && parent.tokenType == KtNodeTypes.LABELED_EXPRESSION) { - labelName = source.treeStructure.findDescendantByType(parent, KtNodeTypes.LABEL).toString() - labelName = labelName.substring(0, labelName.length - 1) + is FirResolvedQualifier -> { + for (reservedUnderscoreDiagnostic in expression.nonFatalDiagnostics.filterIsInstance()) { + reporter.reportOn(reservedUnderscoreDiagnostic.source, FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS, context) } } - - reportIfUnderscore(labelName, expression.source, context, reporter) - } - } - - private fun reportIfUnderscoreInQualifiedAccess( - source: FirPsiSourceElement, - expression: FirStatement, - context: CheckerContext, - reporter: DiagnosticReporter - ) { - fun processQualifiedAccess(psi: PsiElement?) { - if (psi is KtNameReferenceExpression) { - reportIfUnderscore(psi.text, expression.source, context, reporter, isExpression = true) - } else if (psi is KtDotQualifiedExpression || psi is KtCallableReferenceExpression) { - processQualifiedAccess(psi.firstChild) - processQualifiedAccess(psi.lastChild) - } - } - - val psi = source.psi - if (psi.parent !is KtDotQualifiedExpression && psi.parent !is KtCallableReferenceExpression) { - processQualifiedAccess(psi) - } - } - - private fun reportIfUnderscoreInQualifiedAccess( - source: FirLightSourceElement, - expression: FirStatement, - context: CheckerContext, - reporter: DiagnosticReporter - ) { - fun processQualifiedAccess(lightSourceElement: LighterASTNode?) { - val tokenType = lightSourceElement?.tokenType - if (tokenType is KtNameReferenceExpressionElementType) { - reportIfUnderscore(lightSourceElement.toString(), expression.source, context, reporter, isExpression = true) - } else if (lightSourceElement != null && (tokenType is KtDotQualifiedExpressionElementType || tokenType == KtNodeTypes.CALLABLE_REFERENCE_EXPRESSION)) { - val children = lightSourceElement.getChildren(source.treeStructure) - processQualifiedAccess(children.first()) - processQualifiedAccess(children.last()) - } - } - - val astNode = source.lighterASTNode - val parent = source.treeStructure.getParent(astNode) - if (parent?.tokenType !is KtDotQualifiedExpressionElementType && parent?.tokenType != KtNodeTypes.CALLABLE_REFERENCE_EXPRESSION) { - processQualifiedAccess(astNode) } } } @@ -114,27 +40,29 @@ object FirReservedUnderscoreExpressionChecker : FirBasicExpressionChecker() { object FirReservedUnderscoreDeclarationChecker : FirBasicDeclarationChecker() { override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { if ( - declaration is FirClass || - declaration is FirFunction || + declaration is FirRegularClass || declaration is FirTypeParameter || declaration is FirProperty || declaration is FirTypeAlias ) { reportIfUnderscore(declaration, context, reporter) + } else if (declaration is FirFunction) { + if (declaration is FirSimpleFunction) { + reportIfUnderscore(declaration, context, reporter) + } - if (declaration is FirFunction) { - for (parameter in declaration.valueParameters) { - reportIfUnderscore( - parameter, - context, - reporter, - isSingleUnderscoreAllowed = declaration is FirAnonymousFunction || declaration is FirPropertyAccessor - ) - } + val isSingleUnderscoreAllowed = declaration is FirAnonymousFunction || declaration is FirPropertyAccessor + for (parameter in declaration.valueParameters) { + reportIfUnderscore( + parameter, + context, + reporter, + isSingleUnderscoreAllowed = isSingleUnderscoreAllowed + ) } } else if (declaration is FirFile) { for (import in declaration.imports) { - reportIfUnderscore(import.aliasName?.asString(), import.source, context, reporter) + reportIfUnderscore(import.aliasSource, context, reporter, isExpression = false) } } } @@ -146,64 +74,98 @@ private fun reportIfUnderscore( reporter: DiagnosticReporter, isSingleUnderscoreAllowed: Boolean = false ) { - val source = declaration.source - val rawIdentifier = when (source) { + val declarationSource = declaration.source + val rawName = when (declarationSource) { is FirPsiSourceElement -> - (source.psi as? PsiNameIdentifierOwner)?.nameIdentifier?.text + (declarationSource.psi as? PsiNameIdentifierOwner)?.nameIdentifier?.text is FirLightSourceElement -> - source.treeStructure.nameIdentifier(source.lighterASTNode)?.toString() + declarationSource.treeStructure.nameIdentifier(declarationSource.lighterASTNode)?.toString() else -> null } - reportIfUnderscore(rawIdentifier, source, context, reporter, isSingleUnderscoreAllowed) + reportIfUnderscore(rawName, declarationSource, context, reporter, isSingleUnderscoreAllowed) val returnOrReceiverTypeRef = when (declaration) { - is FirValueParameter -> declaration.returnTypeRef.source - is FirFunction -> declaration.receiverTypeRef?.source + is FirValueParameter -> declaration.returnTypeRef + is FirFunction -> declaration.receiverTypeRef else -> null } - val isReportUnderscoreInReturnOrReceiverTypeRef = when (returnOrReceiverTypeRef) { - is FirPsiSourceElement -> { - val psi = returnOrReceiverTypeRef.psi - psi is KtTypeReference && psi.anyDescendantOfType { isUnderscore(it.text) } - } - is FirLightSourceElement -> { - val lighterASTNode = returnOrReceiverTypeRef.lighterASTNode - lighterASTNode.tokenType == KtNodeTypes.TYPE_REFERENCE && - source?.treeStructure?.findFirstDescendant(lighterASTNode) - { it.tokenType == KtNodeTypes.REFERENCE_EXPRESSION && isUnderscore(it.toString()) } != null - } - else -> { - false - } - } + if (returnOrReceiverTypeRef is FirResolvedTypeRef) { + val delegatedTypeRef = returnOrReceiverTypeRef.delegatedTypeRef + if (delegatedTypeRef is FirUserTypeRef) { + for (qualifierPart in delegatedTypeRef.qualifier) { + reportIfUnderscore(qualifierPart.source, context, reporter, isExpression = true) - if (isReportUnderscoreInReturnOrReceiverTypeRef) { - reporter.reportOn(returnOrReceiverTypeRef, FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS, context) + for (typeArgument in qualifierPart.typeArgumentList.typeArguments) { + reportIfUnderscore(typeArgument.source, context, reporter, isExpression = true) + } + } + } } } private fun reportIfUnderscore( - text: CharSequence?, source: FirSourceElement?, context: CheckerContext, reporter: DiagnosticReporter, - isSingleUnderscoreAllowed: Boolean = false, - isExpression: Boolean = false + isExpression: Boolean ) { - if (text == null || isSingleUnderscoreAllowed && text == "_") { + if (source != null && source.kind !is FirFakeSourceElementKind) { + var rawName: String? = null + if (source is FirPsiSourceElement) { + val psi = source.psi + rawName = if (psi is KtNameReferenceExpression) { + psi.getReferencedNameElement().node.text + } else if (psi is KtTypeProjection) { + psi.typeReference?.typeElement?.text + } else if (psi is LeafPsiElement && psi.elementType == KtTokens.IDENTIFIER) { + psi.text + } else { + null + } + } else if (source is FirLightSourceElement) { + val tokenType = source.elementType + rawName = if (tokenType is KtNameReferenceExpressionElementType || tokenType == KtTokens.IDENTIFIER) { + source.lighterASTNode.toString() + } else if (tokenType is KtTypeProjectionElementType) { + source.lighterASTNode.getChildren(source.treeStructure).last().toString() + } else { + null + } + } + + if (rawName?.isUnderscore == true) { + reporter.reportOn( + source, + if (isExpression) FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS else FirErrors.UNDERSCORE_IS_RESERVED, + context + ) + } + } +} + +private fun reportIfUnderscore( + rawName: CharSequence?, + source: FirSourceElement?, + context: CheckerContext, + reporter: DiagnosticReporter, + isSingleUnderscoreAllowed: Boolean +) { + if (source == null || rawName == null || isSingleUnderscoreAllowed && rawName == "_") { return } - if (isUnderscore(text)) { + if (rawName.isUnderscore && source.kind !is FirFakeSourceElementKind) { reporter.reportOn( source, - if (isExpression) FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS else FirErrors.UNDERSCORE_IS_RESERVED, + FirErrors.UNDERSCORE_IS_RESERVED, context ) } } -private fun isUnderscore(text: CharSequence) = text.all { it == '_' } +val CharSequence.isUnderscore: Boolean + get() = all { it == '_' } + diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 653da0dfd3f..bb23ff29008 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -387,6 +387,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_OF import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_ON_LHS_OF_DOT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_VARIANCE_CONFLICT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNDERSCORE_IS_RESERVED +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNEXPECTED_SAFE_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_ENUM_COMPANION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_ENUM_ENTRY @@ -1388,6 +1390,14 @@ class FirDefaultErrorMessages { RENDER_TYPE, RENDER_TYPE, ) + map.put( + UNDERSCORE_IS_RESERVED, + "Names _, __, ___, ..., are reserved in Kotlin" + ) + map.put( + UNDERSCORE_USAGE_WITHOUT_BACKTICKS, + "Names _, __, ___, ... can be used only in back-ticks (`_`, `__`, `___`, ...)" + ) map.put( EQUALITY_NOT_APPLICABLE, "Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''", 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 b8587a5891b..678909e604f 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 @@ -715,20 +715,13 @@ object LightTreePositioningStrategies { endOffset: Int, tree: FlyweightCapableTreeStructure ): List { - if (node.tokenType == KtNodeTypes.RETURN) { - val parent = tree.getParent(node) - if (parent != null) { - val label = tree.findDescendantByType(parent, KtNodeTypes.LABEL) - if (label != null) { - return markElement(label, startOffset, endOffset - 1, tree, node) - } - } + val nameIdentifier = tree.nameIdentifier(node) + if (nameIdentifier != null) { + return markElement(nameIdentifier, startOffset, endOffset, tree, node) + } + if (node.tokenType == KtNodeTypes.LABEL_QUALIFIER) { + return super.mark(node, startOffset, endOffset - 1, tree) } - - val descendants = - tree.collectDescendantsOfType(node, KtTokens.IDENTIFIER) { descendant -> descendant.toString().all { it == '_' } } - if (descendants.isNotEmpty()) - return descendants.map { markSingleElement(it, it, startOffset, endOffset, tree, node) } return super.mark(node, startOffset, endOffset, tree) } } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt index e70936cddb0..786586b0f11 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt @@ -19,10 +19,7 @@ import org.jetbrains.kotlin.fir.declarations.builder.buildAnonymousFunction import org.jetbrains.kotlin.fir.declarations.builder.buildProperty import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl -import org.jetbrains.kotlin.fir.diagnostics.ConeNotAnnotationContainer -import org.jetbrains.kotlin.fir.diagnostics.ConeUnderscoreIsReserved -import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic -import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind +import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock @@ -969,9 +966,16 @@ class ExpressionsConverter( private fun convertSimpleNameExpression(referenceExpression: LighterASTNode): FirQualifiedAccessExpression { return buildQualifiedAccessExpression { source = referenceExpression.toFirSourceElement() + + val nameSource = this@buildQualifiedAccessExpression.source + val rawText = referenceExpression.asText + if (nameSource != null && rawText.isUnderscore) { + nonFatalDiagnostics.add(ConeUnderscoreUsageWithoutBackticks(nameSource)) + } + calleeReference = buildSimpleNamedReference { - source = this@buildQualifiedAccessExpression.source - name = referenceExpression.asText.nameAsSafeName() + source = nameSource + name = rawText.nameAsSafeName() } } } diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index c18eb0a2ce2..0809802d27a 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -1054,7 +1054,7 @@ open class RawFirBuilder( null, ClassKind.CLASS, containerTypeParameters = emptyList(), - containingClassIsExpectClass = false + containingClassIsExpectClass = false ) for (declaration in objectDeclaration.declarations) { @@ -1667,7 +1667,20 @@ open class RawFirBuilder( expression.getQualifiedExpressionForSelector() != null -> expression.parent else -> expression }.toFirSourceElement() - return generateAccessExpression(qualifiedSource, expression.toFirSourceElement(), expression.getReferencedNameAsName()) + + val expressionSource = expression.toFirSourceElement() + var diagnostic: ConeDiagnostic? = null + val rawText = expression.getReferencedNameElement().node.text + if (rawText.isUnderscore) { + diagnostic = ConeUnderscoreUsageWithoutBackticks(expressionSource) + } + + return generateAccessExpression( + qualifiedSource, + expressionSource, + expression.getReferencedNameAsName(), + diagnostic + ) } override fun visitConstantExpression(expression: KtConstantExpression, data: Unit): FirElement = diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt index 67faabb5f1f..bfdee4fd0d2 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.declarations.FirVariable import org.jetbrains.kotlin.fir.declarations.builder.* import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor +import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* @@ -250,7 +251,8 @@ private fun FirExpression.createConventionCall( fun generateAccessExpression( qualifiedSource: FirSourceElement?, calleeReferenceSource: FirSourceElement?, - name: Name + name: Name, + diagnostic: ConeDiagnostic? = null ): FirQualifiedAccessExpression = buildQualifiedAccessExpression { this.source = qualifiedSource @@ -258,6 +260,9 @@ fun generateAccessExpression( this.source = calleeReferenceSource this.name = name } + if (diagnostic != null) { + this.nonFatalDiagnostics.add(diagnostic) + } } fun generateResolvedAccessExpression(source: FirSourceElement?, variable: FirVariable): FirQualifiedAccessExpression = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index d8e96a3df69..e8f54e1ebe6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -844,20 +844,10 @@ object PositioningStrategies { if (nameIdentifier != null) { return super.mark(nameIdentifier) } - } else if (element is KtImportDirective) { - return super.mark(element.alias?.nameIdentifier ?: element) - } else if (element is KtReturnExpression) { - val prevSibling = element.getPrevSiblingIgnoringWhitespace() - if (prevSibling is KtContainerNode) { - return mark(prevSibling) - } - } else if (element !is LeafPsiElement) { - val ranges = element.collectDescendantsOfType { descendant -> descendant.text.all { it == '_' } } - .map { markSingleElement(it) } - if (ranges.isNotEmpty()) { - return ranges - } + } else if (element is KtLabelReferenceExpression) { + return super.mark(element.getReferencedNameElement()) } + return super.mark(element) } } diff --git a/compiler/testData/diagnostics/tests/Underscore.fir.kt b/compiler/testData/diagnostics/tests/Underscore.fir.kt index af1ddccab0b..1e961ffffc7 100644 --- a/compiler/testData/diagnostics/tests/Underscore.fir.kt +++ b/compiler/testData/diagnostics/tests/Underscore.fir.kt @@ -2,9 +2,9 @@ import kotlin.Deprecated as ___ -@___("") data class Pair(val x: Int, val y: Int) +@___("") data class Pair(val x: Int, val y: Int) -class _<________> +class _<________> val ______ = _() fun __(___: Int, y: _?): Int { @@ -25,7 +25,7 @@ fun __(___: Int, y: _: String) +class A1(val _: String) class A2(_: String) { class B { typealias _ = CharSequence @@ -48,3 +48,13 @@ val something2 = doIt { _ -> 1 } var p: Int? get() = null set(_) {} + +object `____` { + object Nested { + fun method() {} + } +} + +fun test() { + ____.Nested.method() +} diff --git a/compiler/testData/diagnostics/tests/Underscore.kt b/compiler/testData/diagnostics/tests/Underscore.kt index 297d213b31c..dfeb9ceb149 100644 --- a/compiler/testData/diagnostics/tests/Underscore.kt +++ b/compiler/testData/diagnostics/tests/Underscore.kt @@ -48,3 +48,13 @@ val something2 = doIt { _ -> 1 } var p: Int? get() = null set(_) {} + +object `____` { + object Nested { + fun method() {} + } +} + +fun test() { + ____.Nested.method() +} diff --git a/compiler/testData/diagnostics/tests/Underscore.txt b/compiler/testData/diagnostics/tests/Underscore.txt index 5265f9662e5..889df562120 100644 --- a/compiler/testData/diagnostics/tests/Underscore.txt +++ b/compiler/testData/diagnostics/tests/Underscore.txt @@ -7,6 +7,7 @@ public val something2: kotlin.Any? public fun __(/*0*/ ___: kotlin.Int, /*1*/ y: _?): kotlin.Int public fun doIt(/*0*/ f: (kotlin.Any?) -> kotlin.Any?): kotlin.Any? public fun oneUnderscore(/*0*/ : kotlin.Int): kotlin.Unit +public fun test(): kotlin.Unit public final class A1 { public constructor A1(/*0*/ _: kotlin.String) @@ -53,3 +54,18 @@ public final class _ { public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } +public object ____ { + private constructor ____() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public object Nested { + private constructor Nested() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun method(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInAnnotation.fir.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInAnnotation.fir.kt deleted file mode 100644 index 8259c0d577d..00000000000 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInAnnotation.fir.kt +++ /dev/null @@ -1,6 +0,0 @@ -package test - -annotation class `__`(val value: String) - -@__("") class TestAnn -@`__`("") class TestAnn2 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInAnnotation.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInAnnotation.kt index 6ecc749c107..d78f8d13a2d 100644 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInAnnotation.kt +++ b/compiler/testData/diagnostics/tests/UnderscoreUsageInAnnotation.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test annotation class `__`(val value: String) diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 88380fc5cab..8ba2a8a9805 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2003,11 +2003,11 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val actualType: KtType } - abstract class UnderscoreIsReserved : KtFirDiagnostic() { + abstract class UnderscoreIsReserved : KtFirDiagnostic() { override val diagnosticClass get() = UnderscoreIsReserved::class } - abstract class UnderscoreUsageWithoutBackticks : KtFirDiagnostic() { + abstract class UnderscoreUsageWithoutBackticks : KtFirDiagnostic() { override val diagnosticClass get() = UnderscoreUsageWithoutBackticks::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index a9c2d056da6..bd2db5934ad 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3228,14 +3228,14 @@ internal class DelegateSpecialFunctionReturnTypeMismatchImpl( internal class UnderscoreIsReservedImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, -) : KtFirDiagnostic.UnderscoreIsReserved(), KtAbstractFirDiagnostic { +) : KtFirDiagnostic.UnderscoreIsReserved(), KtAbstractFirDiagnostic { override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } internal class UnderscoreUsageWithoutBackticksImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken, -) : KtFirDiagnostic.UnderscoreUsageWithoutBackticks(), KtAbstractFirDiagnostic { +) : KtFirDiagnostic.UnderscoreUsageWithoutBackticks(), KtAbstractFirDiagnostic { override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) }