From f9a2d01d57f0e10093bf7dca5390f4b749bcb784 Mon Sep 17 00:00:00 2001 From: vldf Date: Mon, 31 Aug 2020 19:00:43 +0300 Subject: [PATCH] [FIR] Add UselessCallOnNotNullChecker --- .../UselessCallOnNotNullChecker/Basic.kt | 4 ++ .../UselessCallOnNotNullChecker/Basic.txt | 5 ++ .../NotNullType.kt | 4 ++ .../NotNullType.txt | 5 ++ .../NotNullTypeChain.kt | 4 ++ .../NotNullTypeChain.txt | 8 +++ .../NullOrBlankSafe.kt | 4 ++ .../NullOrBlankSafe.txt | 5 ++ .../NullOrEmpty.kt | 4 ++ .../NullOrEmpty.txt | 5 ++ .../NullOrEmptyFake.kt | 4 ++ .../NullOrEmptyFake.txt | 5 ++ .../NullOrEmptySafe.kt | 4 ++ .../NullOrEmptySafe.txt | 5 ++ .../OrEmptyFake.kt | 4 ++ .../OrEmptyFake.txt | 5 ++ .../UselessCallOnNotNullChecker/SafeCall.kt | 4 ++ .../UselessCallOnNotNullChecker/SafeCall.txt | 5 ++ .../UselessCallOnNotNullChecker/Sequence.kt | 5 ++ .../UselessCallOnNotNullChecker/Sequence.txt | 4 ++ .../UselessCallOnNotNullChecker/String.kt | 4 ++ .../UselessCallOnNotNullChecker/String.txt | 5 ++ .../ExtendedFirDiagnosticsTestGenerated.java | 68 +++++++++++++++++++ ...WithLightTreeDiagnosticsTestGenerated.java | 68 +++++++++++++++++++ .../extended/UselessCallOnNotNullChecker.kt | 56 +++++++++++++++ .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../checkers/ExtendedExpressionCheckers.kt | 3 +- 27 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.txt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.kt create mode 100644 compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.txt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UselessCallOnNotNullChecker.kt diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.kt new file mode 100644 index 00000000000..64c24a88ff7 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val list1: List = listOf(1) +val list = list1.orEmpty() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.txt new file mode 100644 index 00000000000..aca20062a2f --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Basic.txt @@ -0,0 +1,5 @@ +FILE: Basic.kt + public final val list1: R|kotlin/collections/List| = R|kotlin/collections/listOf|(Int(1)) + public get(): R|kotlin/collections/List| + public final val list: R|kotlin/collections/List| = R|/list1|.R|kotlin/collections/orEmpty|() + public get(): R|kotlin/collections/List| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.kt new file mode 100644 index 00000000000..64c24a88ff7 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val list1: List = listOf(1) +val list = list1.orEmpty() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.txt new file mode 100644 index 00000000000..50ef18ac31c --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullType.txt @@ -0,0 +1,5 @@ +FILE: NotNullType.kt + public final val list1: R|kotlin/collections/List| = R|kotlin/collections/listOf|(Int(1)) + public get(): R|kotlin/collections/List| + public final val list: R|kotlin/collections/List| = R|/list1|.R|kotlin/collections/orEmpty|() + public get(): R|kotlin/collections/List| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.kt new file mode 100644 index 00000000000..bdfe4fdb129 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val list1: List = listOf(1) +val list = list1.orEmpty().map { "$it" } \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.txt new file mode 100644 index 00000000000..ba45e862468 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NotNullTypeChain.txt @@ -0,0 +1,8 @@ +FILE: NotNullTypeChain.kt + public final val list1: R|kotlin/collections/List| = R|kotlin/collections/listOf|(Int(1)) + public get(): R|kotlin/collections/List| + public final val list: R|kotlin/collections/List| = R|/list1|.R|kotlin/collections/orEmpty|().R|kotlin/collections/map|( = map@fun (it: R|kotlin/Int|): R|kotlin/String| { + ^ R|/it|.R|kotlin/Any.toString|() + } + ) + public get(): R|kotlin/collections/List| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.kt new file mode 100644 index 00000000000..3659d1b1ce8 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val s: String? = "" +val blank = s.isNullOrBlank() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.txt new file mode 100644 index 00000000000..415f8d87957 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrBlankSafe.txt @@ -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| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.kt new file mode 100644 index 00000000000..1f7089702d3 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val s = "" +val empty = s.isNullOrEmpty() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.txt new file mode 100644 index 00000000000..55c33fe10f3 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmpty.txt @@ -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| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.kt new file mode 100644 index 00000000000..157e765883c --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val s: String? = null +val empty = s.isNullOrEmpty() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.txt new file mode 100644 index 00000000000..21cd7aabcd5 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptyFake.txt @@ -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| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.kt new file mode 100644 index 00000000000..15e458bc332 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val s: String? = "" +val empty = s?.isNullOrEmpty() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.txt new file mode 100644 index 00000000000..4c5176e3812 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/NullOrEmptySafe.txt @@ -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?| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.kt new file mode 100644 index 00000000000..75db76908ab --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val list: List? = null +val empty = list.orEmpty() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.txt new file mode 100644 index 00000000000..f36bb26f2dc --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/OrEmptyFake.txt @@ -0,0 +1,5 @@ +FILE: OrEmptyFake.kt + public final val list: R|kotlin/collections/List?| = Null(null) + public get(): R|kotlin/collections/List?| + public final val empty: R|kotlin/collections/List| = R|/list|.R|kotlin/collections/orEmpty|() + public get(): R|kotlin/collections/List| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.kt new file mode 100644 index 00000000000..688b7c51722 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val list1: List? = listOf(1) +val list = list1?.orEmpty() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.txt new file mode 100644 index 00000000000..0c9e988b00c --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/SafeCall.txt @@ -0,0 +1,5 @@ +FILE: SafeCall.kt + public final val list1: R|kotlin/collections/List?| = R|kotlin/collections/listOf|(Int(1)) + public get(): R|kotlin/collections/List?| + public final val list: R|kotlin/collections/List?| = R|/list1|?.{ $subj$.R|kotlin/collections/orEmpty|() } + public get(): R|kotlin/collections/List?| diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.kt new file mode 100644 index 00000000000..e4a155284c8 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(s: Sequence) { + val foo = s.orEmpty() +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.txt new file mode 100644 index 00000000000..4748d8801e7 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/Sequence.txt @@ -0,0 +1,4 @@ +FILE: Sequence.kt + public final fun test(s: R|kotlin/sequences/Sequence|): R|kotlin/Unit| { + lval foo: R|kotlin/sequences/Sequence| = R|/s|.R|kotlin/sequences/orEmpty|() + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.kt new file mode 100644 index 00000000000..63c8dabe169 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val s = "" +val s1 = s.orEmpty() \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.txt new file mode 100644 index 00000000000..56c05d5d60f --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/UselessCallOnNotNullChecker/String.txt @@ -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| diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java index 57d9eaa7c44..e8a12ae97fc 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java @@ -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"); + } + } } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirWithLightTreeDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirWithLightTreeDiagnosticsTestGenerated.java index 32de628438f..79f7c81ec4a 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirWithLightTreeDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirWithLightTreeDiagnosticsTestGenerated.java @@ -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"); + } + } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UselessCallOnNotNullChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UselessCallOnNotNullChecker.kt new file mode 100644 index 00000000000..826de86a6ba --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UselessCallOnNotNullChecker.kt @@ -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" + ) + +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 811edd66ccb..45e39f3187e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -165,4 +165,5 @@ object FirErrors { val ASSIGNED_VALUE_IS_NEVER_READ by warning0() val VARIABLE_INITIALIZER_IS_REDUNDANT by warning0() val VARIABLE_NEVER_READ by warning0() + val USELESS_CALL_ON_NOT_NULL by warning0() } \ No newline at end of file diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/ExtendedExpressionCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/ExtendedExpressionCheckers.kt index 97a474b7ace..e9f482b5573 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/ExtendedExpressionCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/ExtendedExpressionCheckers.kt @@ -23,6 +23,7 @@ object ExtendedExpressionCheckers : ExpressionCheckers() { ) override val qualifiedAccessCheckers: List = listOf( - RedundantCallOfConversionMethod + RedundantCallOfConversionMethod, + UselessCallOnNotNullChecker ) }