FIR checker: warn unnecessary non-null assertions
This commit is contained in:
committed by
Mikhail Glukhikh
parent
5229d4e4f4
commit
2ecb6733ed
Vendored
+2
-2
@@ -11,7 +11,7 @@ fun takeB(b: B) {}
|
||||
|
||||
fun test_1() {
|
||||
val flag = SomeEnum.A1
|
||||
val b: B = when (flag!!) {
|
||||
val b: B = when (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {
|
||||
SomeEnum.A1 -> B()
|
||||
SomeEnum.A2 -> B()
|
||||
}
|
||||
@@ -21,7 +21,7 @@ fun test_1() {
|
||||
fun test_2() {
|
||||
val flag = SomeEnum.A1
|
||||
|
||||
val b = when (flag!!) {
|
||||
val b = when (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {
|
||||
SomeEnum.A1 -> B()
|
||||
SomeEnum.A2 -> B()
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fun get(f: Boolean) = if (f) {A.A1} else {""}
|
||||
<!CONFLICTING_OVERLOADS!>fun case2()<!> {
|
||||
|
||||
val flag: Any = get(false) //string
|
||||
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag!!) { // should be NO_ELSE_IN_WHEN
|
||||
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) { // should be NO_ELSE_IN_WHEN
|
||||
A.A1 -> B()
|
||||
A.A2 -> B()
|
||||
}
|
||||
@@ -30,7 +30,7 @@ fun get(f: Boolean) = if (f) {A.A1} else {""}
|
||||
<!CONFLICTING_OVERLOADS!>fun case2()<!> {
|
||||
|
||||
val flag: Any = get(true) //A
|
||||
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag!!) {// should be NO_ELSE_IN_WHEN
|
||||
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {// should be NO_ELSE_IN_WHEN
|
||||
A.A1 -> B()
|
||||
A.A2 -> B()
|
||||
}
|
||||
@@ -44,7 +44,7 @@ fun get(f: Boolean) = if (f) {A.A1} else {""}
|
||||
fun case3() {
|
||||
|
||||
val flag = "" //A
|
||||
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag!!) {// should be NO_ELSE_IN_WHEN
|
||||
val l1 = <!NO_ELSE_IN_WHEN!>when<!> (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {// should be NO_ELSE_IN_WHEN
|
||||
A.A1 -> B() //should be INCOMPATIBLE_TYPES
|
||||
A.A2 -> B() //should be INCOMPATIBLE_TYPES
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class C(val b: B)
|
||||
// TESTCASE NUMBER: 1
|
||||
fun case1() {
|
||||
val flag = A.A1
|
||||
val l0: B = when (flag!!) {
|
||||
val l0: B = when (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {
|
||||
A.A1 -> B()
|
||||
A.A2 -> B()
|
||||
}
|
||||
@@ -34,7 +34,7 @@ fun case2() {
|
||||
fun case3() {
|
||||
val flag = A.A1
|
||||
|
||||
val l1 = when (flag!!) {
|
||||
val l1 = when (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {
|
||||
A.A1 -> B()
|
||||
A.A2 -> B()
|
||||
}
|
||||
@@ -68,7 +68,7 @@ fun case5() {
|
||||
// TESTCASE NUMBER: 6
|
||||
fun case6() {
|
||||
val flag: Any = A.A1
|
||||
val l1 = when (flag!!) {
|
||||
val l1 = when (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {
|
||||
A.A1 -> B()
|
||||
A.A2 -> B()
|
||||
else -> B()
|
||||
@@ -91,7 +91,7 @@ fun case7() {
|
||||
// TESTCASE NUMBER: 8
|
||||
fun case8() {
|
||||
val flag: Any = A.A1
|
||||
val l1: B = when (flag!!) {
|
||||
val l1: B = when (flag<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>) {
|
||||
A.A1 -> B()
|
||||
A.A2 -> B()
|
||||
else -> B()
|
||||
|
||||
@@ -16,5 +16,5 @@ fun foo() {
|
||||
var c: MutableSet<String>? = null
|
||||
c = HashSet()
|
||||
|
||||
c!!.d = produce()
|
||||
}
|
||||
c<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>.d = produce()
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
fun foo() {
|
||||
val inv = {{}}!!
|
||||
val inv = {{}}<!NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION!>!!<!>
|
||||
val bar = {{}}
|
||||
}
|
||||
|
||||
+5
@@ -560,6 +560,11 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
parameter<FirExpression>("rhs")
|
||||
}
|
||||
// TODO: val UNEXPECTED_SAFE_CALL by ...
|
||||
val UNNECESSARY_NOT_NULL_ASSERTION by warning<FirSourceElement, KtExpression>(PositioningStrategy.OPERATOR) {
|
||||
parameter<ConeKotlinType>("receiverType")
|
||||
}
|
||||
val NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION by warning<FirSourceElement, KtExpression>(PositioningStrategy.OPERATOR)
|
||||
val NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE by warning<FirSourceElement, KtExpression>(PositioningStrategy.OPERATOR)
|
||||
}
|
||||
|
||||
val WHEN_EXPRESSIONS by object : DiagnosticGroup("When expressions") {
|
||||
|
||||
@@ -331,6 +331,9 @@ object FirErrors {
|
||||
val UNSAFE_IMPLICIT_INVOKE_CALL by error1<FirSourceElement, PsiElement, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val UNSAFE_INFIX_CALL by error3<FirSourceElement, KtExpression, FirExpression, String, FirExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val UNSAFE_OPERATOR_CALL by error3<FirSourceElement, KtExpression, FirExpression, String, FirExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
val UNNECESSARY_NOT_NULL_ASSERTION by warning1<FirSourceElement, KtExpression, ConeKotlinType>(SourceElementPositioningStrategies.OPERATOR)
|
||||
val NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION by warning0<FirSourceElement, KtExpression>(SourceElementPositioningStrategies.OPERATOR)
|
||||
val NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE by warning0<FirSourceElement, KtExpression>(SourceElementPositioningStrategies.OPERATOR)
|
||||
|
||||
// When expressions
|
||||
val NO_ELSE_IN_WHEN by error1<FirSourceElement, KtWhenExpression, List<WhenMissingCase>>(SourceElementPositioningStrategies.WHEN_EXPRESSION)
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.declarations.FirAnonymousFunction
|
||||
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
|
||||
import org.jetbrains.kotlin.fir.expressions.FirCheckNotNullCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
|
||||
object FirCheckNotNullCallChecker : FirBasicExpressionChecker() {
|
||||
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (expression !is FirCheckNotNullCall) return
|
||||
|
||||
val argument = expression.argumentList.arguments.singleOrNull() ?: return
|
||||
if (argument is FirAnonymousFunction && argument.isLambda) {
|
||||
reporter.reportOn(expression.source, FirErrors.NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION, context)
|
||||
return
|
||||
}
|
||||
if (argument is FirCallableReferenceAccess) {
|
||||
reporter.reportOn(expression.source, FirErrors.NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE, context)
|
||||
return
|
||||
}
|
||||
// TODO: use of Unit is subject to change.
|
||||
// See BodyResolveComponents.typeForQualifier in ResolveUtils.kt which returns Unit for no value type.
|
||||
if (argument is FirResolvedQualifier && argument.typeRef.isUnit) {
|
||||
// Would be reported as NO_COMPANION_OBJECT
|
||||
return
|
||||
}
|
||||
|
||||
val type = argument.typeRef.coneType.fullyExpandedType(context.session)
|
||||
|
||||
if (!type.canBeNull) {
|
||||
reporter.reportOn(expression.source, FirErrors.UNNECESSARY_NOT_NULL_ASSERTION, type, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -149,6 +149,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOTHING_TO_OVERRI
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_AN_ANNOTATION_CLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_LOOP_LABEL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_SUPERTYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_GET_METHOD
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_SET_METHOD
|
||||
@@ -217,6 +219,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_ON_LHS_OF_DOT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_VARIABLE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_LATEINIT
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_NOT_NULL_ASSERTION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_LABEL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_REFERENCE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_CALL
|
||||
@@ -745,6 +748,9 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
TO_STRING,
|
||||
FIR
|
||||
)
|
||||
map.put(UNNECESSARY_NOT_NULL_ASSERTION, "Unnecessary non-null assertion (!!) on a non-null receiver of type {0}", RENDER_TYPE)
|
||||
map.put(NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION, "Non-null assertion (!!) is called on a lambda expression")
|
||||
map.put(NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE, "Non-null assertion (!!) is called on a callable reference expression")
|
||||
|
||||
// When expressions
|
||||
map.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive, add necessary {0}", WHEN_MISSING_CASES)
|
||||
|
||||
+1
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
|
||||
object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
override val basicExpressionCheckers: Set<FirBasicExpressionChecker> = setOf(
|
||||
FirAnonymousFunctionChecker,
|
||||
FirCheckNotNullCallChecker,
|
||||
FirGetClassCallChecker,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user