[FIR] Implement DIVISION_BY_ZERO diagnostics, fix tests

This commit is contained in:
Ivan Kochurkin
2021-04-08 18:48:42 +03:00
committed by TeamCityServer
parent 964bba3530
commit 847ab40707
14 changed files with 78 additions and 11 deletions
@@ -58,7 +58,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
RETURN_WITH_LABEL,
INT_LITERAL_OUT_OF_RANGE,
FLOAT_LITERAL_OUT_OF_RANGE,
LONG_LITERAL_SUFFIX,
LONG_LITERAL_SUFFIX
;
@@ -64,6 +64,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
val INT_LITERAL_OUT_OF_RANGE by error<FirSourceElement, PsiElement>()
val FLOAT_LITERAL_OUT_OF_RANGE by error<FirSourceElement, PsiElement>()
val WRONG_LONG_SUFFIX by error<FirSourceElement, KtElement>(PositioningStrategy.LONG_LITERAL_SUFFIX)
val DIVISION_BY_ZERO by warning<FirSourceElement, KtExpression>()
}
val UNRESOLVED by object : DiagnosticGroup("Unresolved") {
@@ -89,6 +89,7 @@ object FirErrors {
val INT_LITERAL_OUT_OF_RANGE by error0<FirSourceElement, PsiElement>()
val FLOAT_LITERAL_OUT_OF_RANGE by error0<FirSourceElement, PsiElement>()
val WRONG_LONG_SUFFIX by error0<FirSourceElement, KtElement>(SourceElementPositioningStrategies.LONG_LITERAL_SUFFIX)
val DIVISION_BY_ZERO by warning0<FirSourceElement, KtExpression>()
// Unresolved
val HIDDEN by error1<FirSourceElement, PsiElement, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -0,0 +1,34 @@
/*
* 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 org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
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.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.arguments
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
object FirDivisionByZeroChecker : FirFunctionCallChecker() {
private val defaultPackageName = FqName("kotlin")
private val defaultDivName = Name.identifier("div")
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
val firstValue = (expression.arguments.singleOrNull() as? FirConstExpression<*>)?.value
if (firstValue != null && (firstValue == 0L || firstValue == 0.0f || firstValue == 0.0)) {
val callableId =
((expression.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirNamedFunctionSymbol)?.callableId
if (callableId != null && callableId.packageName == defaultPackageName && callableId.callableName == defaultDivName) {
reporter.reportOn(expression.source, FirErrors.DIVISION_BY_ZERO, context)
}
}
}
}
@@ -32,6 +32,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
override val functionCallCheckers: Set<FirFunctionCallChecker> = setOf(
FirConventionFunctionCallChecker,
FirDivisionByZeroChecker
)
override val tryExpressionCheckers: Set<FirTryExpressionChecker> = setOf(
@@ -1217,7 +1217,8 @@ open class RawFirBuilder(
this.source = source
constructedTypeRef = delegatedType.copyWithNewSourceKind(FirFakeSourceElementKind.ImplicitTypeRef)
this.isThis = isThis
val calleeKind = if (isImplicit)FirFakeSourceElementKind.ImplicitConstructor else FirFakeSourceElementKind.DelegatingConstructorCall
val calleeKind =
if (isImplicit) FirFakeSourceElementKind.ImplicitConstructor else FirFakeSourceElementKind.DelegatingConstructorCall
val calleeSource = this@convert.calleeExpression?.toFirSourceElement(calleeKind)
?: source.fakeElement(calleeKind)
this.calleeReference = if (isThis) {
@@ -15,7 +15,7 @@ fun test3() {}
@Foo(c = [1f], b = [""], a = [1])
fun test4() {}
@Foo([1 + 2], ["Hello, " + "Kotlin"], [1 / 0f])
@Foo([1 + 2], ["Hello, " + "Kotlin"], [<!DIVISION_BY_ZERO!>1 / 0f<!>])
fun test5() {}
const val ONE = 1
@@ -23,7 +23,7 @@ fun test1_2() {}
@Ann2(*[])
fun test2() {}
@Ann3(a = *[0f, 1 / 0f])
@Ann3(a = *[0f, <!DIVISION_BY_ZERO!>1 / 0f<!>])
fun test3() {}
@Ann5(Ann4(*["/"]))
@@ -2,17 +2,21 @@
// !WITH_NEW_INFERENCE
val a1 = 0
val a2 = 1 / 0
val a2 = <!DIVISION_BY_ZERO!>1 / 0<!>
val a3 = 1 / a1
val a4 = 1 / a2
val a5 = 2 * (1 / 0)
val a5 = 2 * (<!DIVISION_BY_ZERO!>1 / 0<!>)
val a6 = 1.div(0)
val a6 = <!DIVISION_BY_ZERO!>1.div(0)<!>
val a7 = 1.div(a1)
val a8 = 1.div(a2)
val a9 = 2 * (1.div(0))
val a9 = 2 * (<!DIVISION_BY_ZERO!>1.div(0)<!>)
val b1: Byte = 1 / 0
@Ann(1 / 0) val b2 = 1
val a10 = <!DIVISION_BY_ZERO!>1 / 0.0f<!>
val a11 = <!DIVISION_BY_ZERO!>1 / 0.0<!>
val a12 = <!DIVISION_BY_ZERO!>1L / 0<!>
annotation class Ann(val i : Int)
val b1: Byte = <!DIVISION_BY_ZERO!>1 / 0<!>
@Ann(<!DIVISION_BY_ZERO!>1 / 0<!>) val b2 = 1
annotation class Ann(val i : Int)
@@ -12,6 +12,10 @@ val a7 = <!DIVISION_BY_ZERO!>1.div(a1)<!>
val a8 = 1.div(a2)
val a9 = 2 * (<!DIVISION_BY_ZERO!>1.div(0)<!>)
val a10 = <!DIVISION_BY_ZERO!>1 / 0.0f<!>
val a11 = <!DIVISION_BY_ZERO!>1 / 0.0<!>
val a12 = <!DIVISION_BY_ZERO!>1L / 0<!>
val b1: Byte = <!DIVISION_BY_ZERO, TYPE_MISMATCH!>1 / 0<!>
@Ann(<!ANNOTATION_ARGUMENT_MUST_BE_CONST, DIVISION_BY_ZERO!>1 / 0<!>) val b2 = 1
@@ -1,6 +1,9 @@
package
public val a1: kotlin.Int = 0
public val a10: kotlin.Float = Infinity.toFloat()
public val a11: kotlin.Double = Infinity.toDouble()
public val a12: kotlin.Long
public val a2: kotlin.Int
public val a3: kotlin.Int
public val a4: kotlin.Int
@@ -19,3 +22,4 @@ public final annotation class Ann : kotlin.Annotation {
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -167,6 +167,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.DIVISION_BY_ZERO) { firDiagnostic ->
DivisionByZeroImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.HIDDEN) { firDiagnostic ->
HiddenImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a.fir as FirDeclaration),
@@ -136,6 +136,10 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = WrongLongSuffix::class
}
abstract class DivisionByZero : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = DivisionByZero::class
}
abstract class Hidden : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = Hidden::class
abstract val hidden: KtSymbol
@@ -194,6 +194,13 @@ internal class WrongLongSuffixImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class DivisionByZeroImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.DivisionByZero(), KtAbstractFirDiagnostic<KtExpression> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class HiddenImpl(
override val hidden: KtSymbol,
firDiagnostic: FirPsiDiagnostic<*>,