[FIR] Implement UNDERSCORE_IS_RESERVED, UNDERSCORE_USAGE_WITHOUT_BACKTICKS diagnostics (psi only)

This commit is contained in:
Ivan Kochurkin
2021-04-22 14:01:02 +03:00
committed by TeamCityServer
parent cc4adb798f
commit ea2d9f7c0c
30 changed files with 291 additions and 121 deletions
@@ -67,6 +67,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
FUN_MODIFIER,
SUSPEND_MODIFIER,
FUN_INTERFACE,
RESERVED_UNDERSCORE,
;
@@ -696,6 +696,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
parameter<String>("expectedFunctionSignature")
parameter<Collection<AbstractFirBasedSymbol<*>>>("candidates")
}
val UNDERSCORE_IS_RESERVED by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
}
val TYPE_ALIAS by object : DiagnosticGroup("Type alias") {
@@ -409,6 +409,8 @@ object FirErrors {
val DELEGATE_SPECIAL_FUNCTION_MISSING by error3<KtExpression, String, ConeKotlinType, String>()
val DELEGATE_SPECIAL_FUNCTION_AMBIGUITY by error2<KtExpression, String, Collection<AbstractFirBasedSymbol<*>>>()
val DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE by error2<KtExpression, String, Collection<AbstractFirBasedSymbol<*>>>()
val UNDERSCORE_IS_RESERVED by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
// Type alias
val TOPLEVEL_TYPEALIASES_ONLY by error0<KtTypeAlias>()
@@ -0,0 +1,143 @@
/*
* Copyright 2010-2021 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.fir.analysis.checkers.expression
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNameIdentifierOwner
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
object FirReservedUnderscoreExpressionChecker : FirBasicExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression is FirFunctionCall) {
reportIfUnderscore(
expression.calleeReference.psi?.text, expression.source, context, reporter,
isExpression = true
)
for (argument in expression.arguments) {
if (argument is FirNamedArgumentExpression) {
reportIfUnderscore(argument.psi?.firstChild?.text, argument.source, context, reporter)
}
}
} else if (expression is FirQualifiedAccess) {
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 = expression.psi
if (psi != null && psi.parent !is KtDotQualifiedExpression && psi.parent !is KtCallableReferenceExpression) {
processQualifiedAccess(psi)
}
} else if (expression is FirGetClassCall) {
for (argument in expression.argumentList.arguments) {
reportIfUnderscore(argument.psi?.text, expression.source, context, reporter, isExpression = true)
}
} else if (expression is FirReturnExpression) {
reportIfUnderscore(expression.target.labelName, expression.source, context, reporter)
}
}
}
object FirReservedUnderscoreDeclarationChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (
declaration is FirClass<*> ||
declaration is FirFunction<*> ||
declaration is FirTypeParameter ||
declaration is FirProperty ||
declaration is FirTypeAlias
) {
reportIfUnderscore(declaration, context, reporter)
if (declaration is FirFunction<*>) {
for (parameter in declaration.valueParameters) {
reportIfUnderscore(
parameter,
context,
reporter,
isSingleUnderscoreAllowed = declaration is FirAnonymousFunction || declaration is FirPropertyAccessor
)
}
}
} else if (declaration is FirFile) {
for (import in declaration.imports) {
reportIfUnderscore(import.aliasName?.asString(), import.source, context, reporter)
}
}
}
}
private fun reportIfUnderscore(
declaration: FirDeclaration,
context: CheckerContext,
reporter: DiagnosticReporter,
isSingleUnderscoreAllowed: Boolean = false
) {
val rawIdentifier = (declaration.psi as? PsiNameIdentifierOwner)?.nameIdentifier?.text ?: return
reportIfUnderscore(rawIdentifier, declaration.source, context, reporter, isSingleUnderscoreAllowed)
fun reportIfAnyDescendantIfUnderscore(typeRef: FirTypeRef?) {
if (typeRef == null) return
if (typeRef.psi?.anyDescendantOfType<LeafPsiElement> { isUnderscore(it.text) } == true) {
reporter.reportOn(
typeRef.source,
FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS,
context
)
}
}
if (declaration is FirValueParameter) {
val psi = declaration.returnTypeRef.psi
if (psi !is KtFunctionLiteral && psi !is KtParameter) {
reportIfAnyDescendantIfUnderscore(declaration.returnTypeRef)
}
} else if (declaration is FirFunction<*>) {
reportIfAnyDescendantIfUnderscore(declaration.receiverTypeRef)
}
}
private fun reportIfUnderscore(
text: String?,
source: FirSourceElement?,
context: CheckerContext,
reporter: DiagnosticReporter,
isSingleUnderscoreAllowed: Boolean = false,
isExpression: Boolean = false
) {
if (text == null || isSingleUnderscoreAllowed && text == "_") {
return
}
if (isUnderscore(text)) {
reporter.reportOn(
source,
if (isExpression) FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS else FirErrors.UNDERSCORE_IS_RESERVED,
context
)
}
}
private fun isUnderscore(text: String) = text.all { it == '_' }
@@ -642,6 +642,9 @@ object LightTreePositioningStrategies {
return markElement(tree.typeParametersList(node) ?: node, startOffset, endOffset, tree, node)
}
}
val RESERVED_UNDERSCORE: LightTreePositioningStrategy = object : LightTreePositioningStrategy() {
}
}
fun FirSourceElement.hasValOrVar(): Boolean =
@@ -228,4 +228,9 @@ object SourceElementPositioningStrategies {
LightTreePositioningStrategies.TYPE_PARAMETERS_LIST,
PositioningStrategies.TYPE_PARAMETERS_LIST
)
val RESERVED_UNDERSCORE = SourceElementPositioningStrategy(
LightTreePositioningStrategies.RESERVED_UNDERSCORE,
PositioningStrategies.RESERVED_UNDERSCORE
)
}
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.analysis.cfa.FirPropertyInitializationAnalyzer
import org.jetbrains.kotlin.fir.analysis.cfa.FirReturnsImpliesAnalyzer
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirReservedUnderscoreDeclarationChecker
object CommonDeclarationCheckers : DeclarationCheckers() {
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker>
@@ -20,6 +21,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
FirConflictsChecker,
FirConflictingProjectionChecker,
FirTypeConstraintsChecker,
FirReservedUnderscoreDeclarationChecker
)
override val memberDeclarationCheckers: Set<FirMemberDeclarationChecker>
@@ -15,6 +15,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
get() = setOf(
FirReservedUnderscoreExpressionChecker
)
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
@@ -31,7 +32,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirTypeParameterInQualifiedAccessChecker,
FirSealedClassConstructorCallChecker,
FirUninitializedEnumChecker,
FirFunInterfaceConstructorReferenceChecker,
FirFunInterfaceConstructorReferenceChecker
)
override val functionCallCheckers: Set<FirFunctionCallChecker>
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNameIdentifierOwner
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.tree.TokenSet
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.cfg.UnreachableCode
@@ -144,10 +145,10 @@ object PositioningStrategies {
ClassKind -> {
val startElement =
element.modifierList?.getModifier(KtTokens.ENUM_KEYWORD)
?: element.modifierList?.getModifier(KtTokens.ANNOTATION_KEYWORD)
?: element.modifierList?.getModifier(KtTokens.ANNOTATION_KEYWORD)
val endElement =
element.node.findChildByType(classKindTokens)?.psi
?: element.nameIdentifier
?: element.nameIdentifier
if (startElement != null && endElement != null) {
return markRange(startElement, endElement)
} else {
@@ -160,7 +161,7 @@ object PositioningStrategies {
}
CallableKind -> {
(callableDeclaration as? KtNamedFunction)?.funKeyword
?: (callableDeclaration as? KtProperty)?.valOrVarKeyword
?: (callableDeclaration as? KtProperty)?.valOrVarKeyword
}
ParameterShape -> {
callableDeclaration?.let { it.receiverTypeReference ?: it.valueParameterList }
@@ -239,7 +240,7 @@ object PositioningStrategies {
val startElement =
element.getModifierList()?.getModifier(KtTokens.ENUM_KEYWORD)
?: element.node.findChildByType(TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD))?.psi
?: element
?: element
return markRange(startElement, nameIdentifier)
}
@@ -264,13 +265,13 @@ object PositioningStrategies {
is KtFunction -> {
val endOfSignatureElement =
element.typeReference
?: element.valueParameterList
?: element.nameIdentifier
?: element
?: element.valueParameterList
?: element.nameIdentifier
?: element
val startElement = if (element is KtFunctionLiteral) {
element.getReceiverTypeReference()
?: element.getValueParameterList()
?: element
?: element.getValueParameterList()
?: element
} else element
return markRange(startElement, endOfSignatureElement)
}
@@ -281,8 +282,8 @@ object PositioningStrategies {
is KtPropertyAccessor -> {
val endOfSignatureElement =
element.returnTypeReference
?: element.rightParenthesis
?: element.namePlaceholder
?: element.rightParenthesis
?: element.namePlaceholder
return markRange(element, endOfSignatureElement)
}
@@ -390,6 +391,7 @@ object PositioningStrategies {
return markElement(nameIdentifier ?: element)
}
}
@JvmField
val FOR_UNRESOLVED_REFERENCE: PositioningStrategy<KtReferenceExpression> = object : PositioningStrategy<KtReferenceExpression>() {
override fun mark(element: KtReferenceExpression): List<TextRange> {
@@ -783,6 +785,31 @@ object PositioningStrategies {
}
}
val RESERVED_UNDERSCORE: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> {
if (element is PsiNameIdentifierOwner) {
val nameIdentifier = element.nameIdentifier
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<LeafPsiElement> { descendant -> descendant.text.all { it == '_' } }
.map { markSingleElement(it) }
if (ranges.isNotEmpty()) {
return ranges
}
}
return super.mark(element)
}
}
@JvmField
val FUN_INTERFACE: PositioningStrategy<KtDeclaration> = object : PositioningStrategy<KtDeclaration>() {
override fun mark(element: KtDeclaration): List<TextRange> {
@@ -43,6 +43,10 @@ fun markElement(element: PsiElement): List<TextRange> {
return listOf(TextRange(getStartOffset(element), getEndOffset(element)))
}
fun markSingleElement(element: PsiElement): TextRange {
return TextRange(getStartOffset(element), getEndOffset(element))
}
fun markNode(node: ASTNode): List<TextRange> {
return markElement(node.psi)
}
+24 -20
View File
@@ -1,46 +1,50 @@
// !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY
import kotlin.Deprecated as ___
import kotlin.Deprecated as <!UNDERSCORE_IS_RESERVED!>___<!>
@___("") data class Pair(val x: Int, val y: Int)
class _<________>
val ______ = _<Int>()
class <!UNDERSCORE_IS_RESERVED, UNDERSCORE_IS_RESERVED!>_<!><<!UNDERSCORE_IS_RESERVED!>________<!>>
val <!UNDERSCORE_IS_RESERVED!>______<!> = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>_<!><Int>()
fun __(___: Int, y: _<Int>?): Int {
val (_, z) = Pair(___ - 1, 42)
val (x, __________) = Pair(___ - 1, 42)
val ____ = x
fun <!UNDERSCORE_IS_RESERVED!>__<!>(<!UNDERSCORE_IS_RESERVED!>___<!>: Int, y: <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>_<!><Int>?): Int {
val (_, z) = Pair(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>___<!> - 1, 42)
val (x, <!UNDERSCORE_IS_RESERVED!>__________<!>) = Pair(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>___<!> - 1, 42)
val <!UNDERSCORE_IS_RESERVED!>____<!> = x
// in backquotes: allowed
val `_` = __________
val `_` = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>__________<!>
val q = fun(_: Int, __: Int) {}
val q = fun(_: Int, <!UNDERSCORE_IS_RESERVED!>__<!>: Int) {}
q(1, 2)
val _ = 56
val <!UNDERSCORE_IS_RESERVED!>_<!> = 56
fun localFun(_: String) = 1
fun localFun(<!UNDERSCORE_IS_RESERVED!>_<!>: String) = 1
__@ return if (y != null) __(____, y) else __(`_`, ______)
<!UNDERSCORE_IS_RESERVED!>__<!>@ return if (y != null) <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>__<!>(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>____<!>, y) else <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>__<!>(`_`, <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>______<!>)
}
class A1(val _: String)
class A2(_: String) {
class A1(val <!UNDERSCORE_IS_RESERVED, UNDERSCORE_IS_RESERVED!>_<!>: String)
class A2(<!UNDERSCORE_IS_RESERVED!>_<!>: String) {
class B {
typealias _ = CharSequence
typealias <!UNDERSCORE_IS_RESERVED!>_<!> = CharSequence
}
val _: Int = 1
val <!UNDERSCORE_IS_RESERVED!>_<!>: Int = 1
fun _() {}
fun <!UNDERSCORE_IS_RESERVED!>_<!>() {}
fun foo(_: Double) {}
fun foo(<!UNDERSCORE_IS_RESERVED!>_<!>: Double) {}
}
// one underscore parameters for named function are still prohibited
fun oneUnderscore(_: Int) {}
fun oneUnderscore(<!UNDERSCORE_IS_RESERVED!>_<!>: Int) {}
fun doIt(f: (Any?) -> Any?) = f(null)
val something = doIt { __ -> __ }
val something = doIt { <!UNDERSCORE_IS_RESERVED!>__<!> -> <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>__<!> }
val something2 = doIt { _ -> 1 }
var p: Int?
get() = null
set(_) {}
+4
View File
@@ -44,3 +44,7 @@ fun doIt(f: (Any?) -> Any?) = f(null)
val something = doIt { <!UNDERSCORE_IS_RESERVED!>__<!> -> <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>__<!> }
val something2 = doIt { _ -> 1 }
var p: Int?
get() = null
set(_) {}
@@ -1,42 +0,0 @@
// !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY
fun test(`_`: Int) {
_ + 1
`_` + 1
}
fun `__`() {}
fun testCall() {
__()
`__`()
}
val testCallableRef = ::__
val testCallableRef2 = ::`__`
object Host {
val `_` = 42
object `__` {
val bar = 4
}
}
val testQualified = Host._
val testQualified2 = Host.`_`
object `___` {
val test = 42
}
val testQualifier = ___.test
val testQualifier2 = `___`.test
val testQualifier3 = Host.__.bar
val testQualifier4 = Host.`__`.bar
fun testCallableRefLHSValue(`_`: Any) = _::toString
fun testCallableRefLHSValue2(`_`: Any) = `_`::toString
val testCallableRefLHSObject = ___::toString
val testCallableRefLHSObject2 = `___`::toString
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY
fun test(`_`: Int) {
@@ -1,11 +0,0 @@
class `___` {
class `____`
}
val testCallableRefLHSType = ___::toString
val testCallableRefLHSType2 = `___`::toString
val testClassLiteralLHSType = ___::class
val testClassLiteralLHSType2 = `___`::class
val tesLHSTypeFQN = `___`.____::class
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class `___` {
class `____`
}
@@ -1,12 +0,0 @@
// !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY
class `_`<`__`> {
fun testTypeArgument(x: List<__>) = x
fun testTypeArgument2(x: List<`__`>) = x
}
fun _<Any>.testTypeConstructor() {}
fun `_`<Any>.testTypeConstructor2() {}
val testConstructor = _<Any>()
val testConstructor2 = `_`<Any>()
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY
class `_`<`__`> {
@@ -1,6 +0,0 @@
object Host {
val `____` = { -> }
fun testFunTypeVal() {
____()
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
object Host {
val `____` = { -> }
fun testFunTypeVal() {
@@ -11,39 +11,39 @@ class C {
fun test() {
for ((x, _) in C()) {
foo(x, <!UNRESOLVED_REFERENCE!>_<!>)
foo(x, <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>)
}
for ((_, y) in C()) {
foo(<!UNRESOLVED_REFERENCE!>_<!>, y)
foo(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>, y)
}
for ((_, _) in C()) {
foo(<!UNRESOLVED_REFERENCE!>_<!>, <!UNRESOLVED_REFERENCE!>_<!>)
foo(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>, <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>)
}
for ((_ : Int, _ : String) in C()) {
foo(<!UNRESOLVED_REFERENCE!>_<!>, <!UNRESOLVED_REFERENCE!>_<!>)
foo(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>, <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>)
}
for ((_ : String, _ : Int) in C()) {
foo(<!UNRESOLVED_REFERENCE!>_<!>, <!UNRESOLVED_REFERENCE!>_<!>)
foo(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>, <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>)
}
val (x, _) = A()
val (_, y) = A()
foo(x, y)
foo(x, <!UNRESOLVED_REFERENCE!>_<!>)
foo(<!UNRESOLVED_REFERENCE!>_<!>, y)
foo(x, <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>)
foo(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>, y)
val (`_`, z) = A()
foo(_, z)
foo(<!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>_<!>, z)
val (_, `_`) = A()
foo(<!ARGUMENT_TYPE_MISMATCH!>_<!>, y)
foo(<!ARGUMENT_TYPE_MISMATCH, UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>_<!>, y)
val (unused, _) = A()
}
@@ -29,7 +29,7 @@ fun foo() {
}
} catch (_: Exception) {
`_`.stackTrace
val y1 = _
val y1 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>_<!>
val y2 = (`_`)
}
try {
@@ -29,7 +29,7 @@ fun foo() {
}
} catch (_: Exception) {
`_`.stackTrace
val y1 = _
val y1 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>_<!>
val y2 = (`_`)
}
try {
@@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
import org.jetbrains.kotlin.checkers.utils.TypeOfCall
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
@@ -70,7 +72,10 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
for (file in module.files) {
val firFile = info.firFiles[file] ?: continue
val diagnostics = diagnosticsPerFile[firFile] ?: continue
var diagnostics = diagnosticsPerFile[firFile] ?: continue
if (AdditionalFilesDirectives.CHECK_TYPE in module.directives) {
diagnostics = diagnostics.filter { it.factory.name != Errors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name }
}
val diagnosticsMetadataInfos = diagnostics.mapNotNull { diagnostic ->
if (!diagnosticsService.shouldRenderDiagnostic(module, diagnostic.factory.name)) return@mapNotNull null
// SYNTAX errors will be reported later
@@ -21,10 +21,10 @@ val value_3 = <!UNRESOLVED_REFERENCE!>_____________0000<!>
val value_4 = <!UNRESOLVED_REFERENCE!>_______________________________________________________________________________________________________________________________________________________0<!>
// TESTCASE NUMBER: 5
val value_5 = <!UNRESOLVED_REFERENCE!>____________________________________________________<!>
val value_5 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>____________________________________________________<!>
// TESTCASE NUMBER: 6
val value_6 = <!UNRESOLVED_REFERENCE!>_<!>
val value_6 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>
// TESTCASE NUMBER: 7
val value_7 = <!UNRESOLVED_REFERENCE!>_0_<!>
@@ -21,10 +21,10 @@ val value_3 = <!UNRESOLVED_REFERENCE!>_____________0000<!>
val value_4 = <!UNRESOLVED_REFERENCE!>_______________________________________________________________________________________________________________________________________________________0<!>
// TESTCASE NUMBER: 5
val value_5 = <!UNRESOLVED_REFERENCE!>____________________________________________________<!>
val value_5 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>____________________________________________________<!>
// TESTCASE NUMBER: 6
val value_6 = <!UNRESOLVED_REFERENCE!>_<!>
val value_6 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>
// TESTCASE NUMBER: 7
val value_7 = <!UNRESOLVED_REFERENCE!>_0_<!>
@@ -21,10 +21,10 @@ val value_3 = <!UNRESOLVED_REFERENCE!>_____________0000<!>
val value_4 = <!UNRESOLVED_REFERENCE!>_______________________________________________________________________________________________________________________________________________________0<!>
// TESTCASE NUMBER: 5
val value_5 = <!UNRESOLVED_REFERENCE!>____________________________________________________<!>
val value_5 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>____________________________________________________<!>
// TESTCASE NUMBER: 6
val value_6 = <!UNRESOLVED_REFERENCE!>_<!>
val value_6 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS, UNRESOLVED_REFERENCE!>_<!>
// TESTCASE NUMBER: 7
val value_7 = <!UNRESOLVED_REFERENCE!>_0_<!>
@@ -1960,6 +1960,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.UNDERSCORE_IS_RESERVED) { firDiagnostic ->
UnderscoreIsReservedImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS) { firDiagnostic ->
UnderscoreUsageWithoutBackticksImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.TOPLEVEL_TYPEALIASES_ONLY) { firDiagnostic ->
ToplevelTypealiasesOnlyImpl(
firDiagnostic as FirPsiDiagnostic<*>,
@@ -1371,6 +1371,14 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val candidates: List<KtSymbol>
}
abstract class UnderscoreIsReserved : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = UnderscoreIsReserved::class
}
abstract class UnderscoreUsageWithoutBackticks : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = UnderscoreUsageWithoutBackticks::class
}
abstract class ToplevelTypealiasesOnly : KtFirDiagnostic<KtTypeAlias>() {
override val diagnosticClass get() = ToplevelTypealiasesOnly::class
}
@@ -2224,6 +2224,20 @@ internal class DelegateSpecialFunctionNoneApplicableImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class UnderscoreIsReservedImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.UnderscoreIsReserved(), KtAbstractFirDiagnostic<KtExpression> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class UnderscoreUsageWithoutBackticksImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.UnderscoreUsageWithoutBackticks(), KtAbstractFirDiagnostic<KtExpression> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class ToplevelTypealiasesOnlyImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,