[FIR] Add UselessCallOnNotNullChecker
This commit is contained in:
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val list1: List<Int> = listOf(1)
|
||||
val list = list1.<!USELESS_CALL_ON_NOT_NULL!>orEmpty()<!>
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
FILE: Basic.kt
|
||||
public final val list1: R|kotlin/collections/List<kotlin/Int>| = R|kotlin/collections/listOf|<R|kotlin/Int|>(Int(1))
|
||||
public get(): R|kotlin/collections/List<kotlin/Int>|
|
||||
public final val list: R|kotlin/collections/List<kotlin/Int>| = R|/list1|.R|kotlin/collections/orEmpty|<R|kotlin/Int|>()
|
||||
public get(): R|kotlin/collections/List<kotlin/Int>|
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val list1: List<Int> = listOf(1)
|
||||
val list = list1.<!USELESS_CALL_ON_NOT_NULL!>orEmpty()<!>
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
FILE: NotNullType.kt
|
||||
public final val list1: R|kotlin/collections/List<kotlin/Int>| = R|kotlin/collections/listOf|<R|kotlin/Int|>(Int(1))
|
||||
public get(): R|kotlin/collections/List<kotlin/Int>|
|
||||
public final val list: R|kotlin/collections/List<kotlin/Int>| = R|/list1|.R|kotlin/collections/orEmpty|<R|kotlin/Int|>()
|
||||
public get(): R|kotlin/collections/List<kotlin/Int>|
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val list1: List<Int> = listOf(1)
|
||||
val list = list1.<!USELESS_CALL_ON_NOT_NULL!>orEmpty()<!>.map { "$it" }
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
FILE: NotNullTypeChain.kt
|
||||
public final val list1: R|kotlin/collections/List<kotlin/Int>| = R|kotlin/collections/listOf|<R|kotlin/Int|>(Int(1))
|
||||
public get(): R|kotlin/collections/List<kotlin/Int>|
|
||||
public final val list: R|kotlin/collections/List<kotlin/String>| = R|/list1|.R|kotlin/collections/orEmpty|<R|kotlin/Int|>().R|kotlin/collections/map|<R|kotlin/Int|, R|kotlin/String|>(<L> = map@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/String| <kind=UNKNOWN> {
|
||||
^ R|<local>/it|.R|kotlin/Any.toString|()
|
||||
}
|
||||
)
|
||||
public get(): R|kotlin/collections/List<kotlin/String>|
|
||||
compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.kt
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val s: String? = ""
|
||||
val blank = s.isNullOrBlank()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
FILE: NullOrBlankSafe.kt
|
||||
public final val s: R|kotlin/String?| = String()
|
||||
public get(): R|kotlin/String?|
|
||||
public final val blank: R|kotlin/Boolean| = R|/s|.R|kotlin/text/isNullOrBlank|()
|
||||
public get(): R|kotlin/Boolean|
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val s = ""
|
||||
val empty = s.<!USELESS_CALL_ON_NOT_NULL!>isNullOrEmpty()<!>
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
FILE: NullOrEmpty.kt
|
||||
public final val s: R|kotlin/String| = String()
|
||||
public get(): R|kotlin/String|
|
||||
public final val empty: R|kotlin/Boolean| = R|/s|.R|kotlin/text/isNullOrEmpty|()
|
||||
public get(): R|kotlin/Boolean|
|
||||
compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.kt
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val s: String? = null
|
||||
val empty = s.isNullOrEmpty()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
FILE: NullOrEmptyFake.kt
|
||||
public final val s: R|kotlin/String?| = Null(null)
|
||||
public get(): R|kotlin/String?|
|
||||
public final val empty: R|kotlin/Boolean| = R|/s|.R|kotlin/text/isNullOrEmpty|()
|
||||
public get(): R|kotlin/Boolean|
|
||||
compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.kt
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val s: String? = ""
|
||||
val empty = s?.<!USELESS_CALL_ON_NOT_NULL!>isNullOrEmpty()<!>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
FILE: NullOrEmptySafe.kt
|
||||
public final val s: R|kotlin/String?| = String()
|
||||
public get(): R|kotlin/String?|
|
||||
public final val empty: R|kotlin/Boolean?| = R|/s|?.{ $subj$.R|kotlin/text/isNullOrEmpty|() }
|
||||
public get(): R|kotlin/Boolean?|
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val list: List<String>? = null
|
||||
val empty = list.orEmpty()
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
FILE: OrEmptyFake.kt
|
||||
public final val list: R|kotlin/collections/List<kotlin/String>?| = Null(null)
|
||||
public get(): R|kotlin/collections/List<kotlin/String>?|
|
||||
public final val empty: R|kotlin/collections/List<kotlin/String>| = R|/list|.R|kotlin/collections/orEmpty|<R|kotlin/String|>()
|
||||
public get(): R|kotlin/collections/List<kotlin/String>|
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val list1: List<Int>? = listOf(1)
|
||||
val list = list1?.<!USELESS_CALL_ON_NOT_NULL!>orEmpty()<!>
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
FILE: SafeCall.kt
|
||||
public final val list1: R|kotlin/collections/List<kotlin/Int>?| = R|kotlin/collections/listOf|<R|kotlin/Int|>(Int(1))
|
||||
public get(): R|kotlin/collections/List<kotlin/Int>?|
|
||||
public final val list: R|kotlin/collections/List<kotlin/Int>?| = R|/list1|?.{ $subj$.R|kotlin/collections/orEmpty|<R|kotlin/Int|>() }
|
||||
public get(): R|kotlin/collections/List<kotlin/Int>?|
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun test(s: Sequence<Int>) {
|
||||
val <!UNUSED_VARIABLE!>foo<!> = s.<!USELESS_CALL_ON_NOT_NULL!>orEmpty()<!>
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
FILE: Sequence.kt
|
||||
public final fun test(s: R|kotlin/sequences/Sequence<kotlin/Int>|): R|kotlin/Unit| {
|
||||
lval foo: R|kotlin/sequences/Sequence<kotlin/Int>| = R|<local>/s|.R|kotlin/sequences/orEmpty|<R|kotlin/Int|>()
|
||||
}
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val s = ""
|
||||
val s1 = s.<!USELESS_CALL_ON_NOT_NULL!>orEmpty()<!>
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
FILE: String.kt
|
||||
public final val s: R|kotlin/String| = String()
|
||||
public get(): R|kotlin/String|
|
||||
public final val s1: R|kotlin/String| = R|/s|.R|kotlin/text/orEmpty|()
|
||||
public get(): R|kotlin/String|
|
||||
Generated
+68
@@ -349,4 +349,72 @@ public class ExtendedFirDiagnosticsTestGenerated extends AbstractExtendedFirDiag
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/valueIsNeverRead.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class UselessCallOnNotNullChecker extends AbstractExtendedFirDiagnosticsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInUselessCallOnNotNullChecker() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("Basic.kt")
|
||||
public void testBasic() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NotNullType.kt")
|
||||
public void testNotNullType() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NotNullTypeChain.kt")
|
||||
public void testNotNullTypeChain() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NullOrBlankSafe.kt")
|
||||
public void testNullOrBlankSafe() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NullOrEmpty.kt")
|
||||
public void testNullOrEmpty() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NullOrEmptyFake.kt")
|
||||
public void testNullOrEmptyFake() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NullOrEmptySafe.kt")
|
||||
public void testNullOrEmptySafe() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("OrEmptyFake.kt")
|
||||
public void testOrEmptyFake() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SafeCall.kt")
|
||||
public void testSafeCall() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Sequence.kt")
|
||||
public void testSequence() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("String.kt")
|
||||
public void testString() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+68
@@ -349,4 +349,72 @@ public class ExtendedFirWithLightTreeDiagnosticsTestGenerated extends AbstractEx
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/unused/valueIsNeverRead.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class UselessCallOnNotNullChecker extends AbstractExtendedFirWithLightTreeDiagnosticsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInUselessCallOnNotNullChecker() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("Basic.kt")
|
||||
public void testBasic() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NotNullType.kt")
|
||||
public void testNotNullType() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NotNullTypeChain.kt")
|
||||
public void testNotNullTypeChain() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NullOrBlankSafe.kt")
|
||||
public void testNullOrBlankSafe() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NullOrEmpty.kt")
|
||||
public void testNullOrEmpty() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NullOrEmptyFake.kt")
|
||||
public void testNullOrEmptyFake() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NullOrEmptySafe.kt")
|
||||
public void testNullOrEmptySafe() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("OrEmptyFake.kt")
|
||||
public void testOrEmptyFake() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SafeCall.kt")
|
||||
public void testSafeCall() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Sequence.kt")
|
||||
public void testSequence() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("String.kt")
|
||||
public void testString() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.extended
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeNullability
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.classId
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
|
||||
object UselessCallOnNotNullChecker : FirQualifiedAccessChecker() {
|
||||
// todo: add 'call may be reduced' in cases like 's?.isNullOrEmpty()' where 's: String? = ""'
|
||||
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val method = expression.getCallableId() ?: return
|
||||
val calleeOn = expression.explicitReceiver ?: return
|
||||
val calleePackageName = calleeOn.getPackage()
|
||||
val calleeName = method.callableName.asString()
|
||||
if ("$calleePackageName.$calleeName" !in triggerOn) return
|
||||
|
||||
if (calleeOn.getNullability() == ConeNullability.NOT_NULL) {
|
||||
reporter.report(expression.source, FirErrors.USELESS_CALL_ON_NOT_NULL)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirQualifiedAccessExpression.getCallableId() =
|
||||
((calleeReference as? FirResolvedNamedReference)?.resolvedSymbol as? FirNamedFunctionSymbol)?.callableId
|
||||
|
||||
private fun FirExpression.getPackage() =
|
||||
(typeRef as? FirResolvedTypeRef)?.coneType?.classId?.packageFqName.toString()
|
||||
|
||||
private fun FirExpression.getNullability() = (typeRef as FirResolvedTypeRef).type.nullability
|
||||
|
||||
|
||||
private val triggerOn = setOf(
|
||||
"kotlin.collections.orEmpty",
|
||||
"kotlin.sequences.orEmpty",
|
||||
"kotlin.text.orEmpty",
|
||||
"kotlin.text.isNullOrEmpty",
|
||||
"kotlin.text.isNullOrEmpty",
|
||||
"kotlin.text.isNullOrBlank",
|
||||
"kotlin.isNullOrBlank",
|
||||
"kotlin.isNullOrEmpty",
|
||||
"kotlin.orEmpty"
|
||||
)
|
||||
|
||||
}
|
||||
@@ -165,4 +165,5 @@ object FirErrors {
|
||||
val ASSIGNED_VALUE_IS_NEVER_READ by warning0<FirSourceElement, PsiElement>()
|
||||
val VARIABLE_INITIALIZER_IS_REDUNDANT by warning0<FirSourceElement, PsiElement>()
|
||||
val VARIABLE_NEVER_READ by warning0<FirSourceElement, PsiElement>()
|
||||
val USELESS_CALL_ON_NOT_NULL by warning0<FirSourceElement, PsiElement>()
|
||||
}
|
||||
+2
-1
@@ -23,6 +23,7 @@ object ExtendedExpressionCheckers : ExpressionCheckers() {
|
||||
)
|
||||
|
||||
override val qualifiedAccessCheckers: List<FirQualifiedAccessChecker> = listOf(
|
||||
RedundantCallOfConversionMethod
|
||||
RedundantCallOfConversionMethod,
|
||||
UselessCallOnNotNullChecker
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user