diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ExplicitCollectionElementAccessMethodSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ExplicitCollectionElementAccessMethodSpec.kt index b43e45f2c96..c6d0394e1cb 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ExplicitCollectionElementAccessMethodSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ExplicitCollectionElementAccessMethodSpec.kt @@ -1,477 +1,469 @@ package io.gitlab.arturbosch.detekt.rules.style -import io.github.detekt.test.utils.createEnvironment -import io.github.detekt.test.utils.resourceAsPath import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext import io.gitlab.arturbosch.detekt.test.lintWithContext import org.assertj.core.api.Assertions.assertThat import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test -@KotlinCoreEnvironmentTest -class ExplicitCollectionElementAccessMethodSpec(val env: KotlinCoreEnvironment) { +class ExplicitCollectionElementAccessMethodSpec { val subject = ExplicitCollectionElementAccessMethod(Config.empty) @Nested - inner class `Kotlin map` { + @KotlinCoreEnvironmentTest + inner class WithDefaultSources(val env: KotlinCoreEnvironment) { + @Nested + inner class `Kotlin map` { - @Test - fun `reports map element access with get method`() { - val code = """ + @Test + fun `reports map element access with get method`() { + val code = """ fun f() { val map = mapOf() val value = map.get("key") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `does not report safe map element access`() { - val code = """ + @Test + fun `does not report safe map element access`() { + val code = """ fun f() { val map = mapOf() val value = map?.get("key") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - fun `reports map set method usage with unused return value`() { - val code = """ + @Test + fun `reports map set method usage with unused return value`() { + val code = """ fun f() { val map = mutableMapOf() map.set("key", "value") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `reports map put method usage with unused return value`() { - val code = """ + @Test + fun `reports map put method usage with unused return value`() { + val code = """ fun f() { val map = mutableMapOf() map.put("key", "val") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `does not report map put method usage with variable assignment`() { - val code = """ + @Test + fun `does not report map put method usage with variable assignment`() { + val code = """ fun f() { val map = mutableMapOf() val oldValue = map.put("key", "val") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - fun `does not report map put method with used return value`() { - val code = """ + @Test + fun `does not report map put method with used return value`() { + val code = """ fun f(): Boolean { val map = mutableMapOf() return map.put("key", "val") == null } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - fun `reports map element access with get method of non-abstract map`() { - val code = """ + @Test + fun `reports map element access with get method of non-abstract map`() { + val code = """ fun f() { val map = hashMapOf() val value = map.get("key") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `reports map element insert with put method of non-abstract map`() { - val code = """ + @Test + fun `reports map element insert with put method of non-abstract map`() { + val code = """ fun f() { val map = hashMapOf() map.put("key", "value") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - @DisplayName("does not report map access with []") - fun noReportMapAccessWithBrackets() { - val code = """ + @Test + @DisplayName("does not report map access with []") + fun noReportMapAccessWithBrackets() { + val code = """ fun f() { val map = mapOf() val value = map["key"] } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - @DisplayName("does not report map insert with []") - fun noReportMapInsertWithBrackets() { - val code = """ + @Test + @DisplayName("does not report map insert with []") + fun noReportMapInsertWithBrackets() { + val code = """ fun f() { val map = mutableMapOf() map["key"] = "value" } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - fun `reports map element access with get method from map in a chain`() { - val code = """ + @Test + fun `reports map element access with get method from map in a chain`() { + val code = """ fun f() { val map = mapOf() val value = listOf("1", "2").associateBy { it }.get("1") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `reports map element access with get method from non-abstract map`() { - val code = """ + @Test + fun `reports map element access with get method from non-abstract map`() { + val code = """ fun f() { val map = linkedMapOf() val value = map.get("key") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `does not report calls on implicit receiver`() { - val code = """ - fun f() { - val map = mapOf() - with(map) { get("a") } - } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(0) + @Test + fun `does not report calls on implicit receiver`() { + val code = """ + fun f() { + val map = mapOf() + with(map) { get("a") } + } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(0) + } } - } - @Nested - inner class `Kotlin list` { - @Test - fun `reports list element access with get method`() { - val code = """ + @Nested + inner class `Kotlin list` { + @Test + fun `reports list element access with get method`() { + val code = """ fun f() { val list = listOf() val value = list.get(0) } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `reports mutable list element access with get method`() { - val code = """ + @Test + fun `reports mutable list element access with get method`() { + val code = """ fun f() { val list = mutableListOf() val value = list.get(0) } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - @DisplayName("does not report element access with []") - fun noReportElementAccessWithBrackets() { - val code = """ + @Test + @DisplayName("does not report element access with []") + fun noReportElementAccessWithBrackets() { + val code = """ fun f() { val list = listOf() val value = list[0] } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - fun `reports element access with get method of non-abstract list`() { - val code = """ + @Test + fun `reports element access with get method of non-abstract list`() { + val code = """ fun f() { val list = arrayListOf() val value = list.get(0) } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `does not report calls on implicit receiver`() { - val code = """ - fun f() { - val list = listOf() - val value = with(list) { get(0) } - } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(0) + @Test + fun `does not report calls on implicit receiver`() { + val code = """ + fun f() { + val list = listOf() + val value = with(list) { get(0) } + } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(0) + } } - } - @Nested - inner class `Java map` { + @Nested + inner class `Java map` { - @Test - fun `reports map element access with get method`() { - val code = """ + @Test + fun `reports map element access with get method`() { + val code = """ fun f() { val map = java.util.HashMap() val value = map.get("key") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `reports map set method usage with unused return value`() { - val code = """ + @Test + fun `reports map set method usage with unused return value`() { + val code = """ fun f() { val map = java.util.HashMap() map.set("key", "val") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `reports map put method usage with unused return value`() { - val code = """ + @Test + fun `reports map put method usage with unused return value`() { + val code = """ fun f() { val map = java.util.HashMap() map.put("key", "val") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - @DisplayName("does not report map access with []") - fun noReportMapAccessWithBrackets() { - val code = """ + @Test + @DisplayName("does not report map access with []") + fun noReportMapAccessWithBrackets() { + val code = """ fun f() { val map = java.util.HashMap() val value = map["key"] } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - @DisplayName("does not report map insert with []") - fun noReportMapInsertWithBrackets() { - val code = """ + @Test + @DisplayName("does not report map insert with []") + fun noReportMapInsertWithBrackets() { + val code = """ fun f() { val map = java.util.HashMap() map["key"] = "value" } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - fun `reports map element access with get method from map in a chain`() { - val code = """ + @Test + fun `reports map element access with get method from map in a chain`() { + val code = """ fun f() { val map = java.util.HashMap() val value = listOf("1", "2").associateBy { it }.get("1") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } } - } - @Nested - inner class `custom operators` { + @Nested + inner class `custom operators` { - @Test - fun `reports custom get operator`() { - val code = """ + @Test + fun `reports custom get operator`() { + val code = """ class Custom { operator fun get(i: Int) = 42 } fun f() { val custom = Custom() val value = custom.get(0) } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `does not report non-operator get method`() { - val code = """ + @Test + fun `does not report non-operator get method`() { + val code = """ class Custom { fun get(i: Int) = 42 } fun f() { val custom = Custom() val value = custom.get(0) } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - fun `reports custom set operator with unused return value`() { - val code = """ + @Test + fun `reports custom set operator with unused return value`() { + val code = """ class Custom { operator fun set(key: String, value: String) {} } fun f() { val custom = Custom() custom.set("key", "value") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - fun `does not report non-operator set method`() { - val code = """ + @Test + fun `does not report non-operator set method`() { + val code = """ class Custom { fun set(key: String, value: String) {} } fun f() { val custom = Custom() custom.set("key", "value") } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @Test - fun `does not report custom get operator with type parameters`() { - val code = """ - class C { - operator fun get(key: String): List? = null - } - fun test(c: C) { - c.get("key") - } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + @Test + fun `does not report custom get operator with type parameters`() { + val code = """ + class C { + operator fun get(key: String): List? = null + } + fun test(c: C) { + c.get("key") + } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } } - } - @Nested - inner class `Java list` { + @Nested + inner class `Java list` { - @Test - fun `reports list element access with get method`() { - val code = """ + @Test + fun `reports list element access with get method`() { + val code = """ fun f() { val list = java.util.ArrayList() val value = list.get(0) } - """ - assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) - } + """ + assertThat(subject.compileAndLintWithContext(env, code)).hasSize(1) + } - @Test - @DisplayName("does not report element access with []") - fun noReportElementAccessWithBrackets() { - val code = """ + @Test + @DisplayName("does not report element access with []") + fun noReportElementAccessWithBrackets() { + val code = """ fun f() { val list = java.util.ArrayList() val value = list[0] } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } - } - - @Nested - inner class `edge cases` { - - @Test - fun `does not crash for getter`() { - val code = """ - class A { - val i: Int get() = 1 + 2 - val c: Char? get() = "".first() ?: throw IllegalArgumentException("getter") - } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } - - @Test - fun `does not crash for fluent api`() { - val code = """ - val string = "" - .toString() - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() - } - - @Test - fun `does not report for unresolvable code`() { - val code = """ - fun f() { - val unknownType = UnknownType() - val value = unknownType.put("answer", 42) - } - """ - assertThat(subject.lintWithContext(env, code)).isEmpty() - } - - @Test - fun `does not report for put functions without caller`() { - val code = """ - fun put() { } - fun f() { - put() - } - """ - assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } } @Nested - inner class JavaSourceTests { + inner class `edge cases` { - private val environmentWrapper = - createEnvironment(additionalJavaSourceRootPaths = listOf(resourceAsPath("java").toFile())) - private val customEnv = environmentWrapper.env + @Test + fun `does not crash for getter`() { + val code = """ + class A { + val i: Int get() = 1 + 2 + val c: Char? get() = "".first() ?: throw IllegalArgumentException("getter") + } + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } - @AfterAll - fun disposeEnvironment() { - environmentWrapper.dispose() + @Test + fun `does not crash for fluent api`() { + val code = """ + val string = "" + .toString() + """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() } @Test - fun `reports setter from java with 2 or less parameters`() { - // this test case ensures that the test environment are set up correctly. + fun `does not report for unresolvable code`() { val code = """ - import com.example.fromjava.Rect - - fun foo() { - val rect = Rect() - rect.set(0, 1) - } + fun f() { + val unknownType = UnknownType() + val value = unknownType.put("answer", 42) + } """ - assertThat(subject.lintWithContext(customEnv, code)).hasSize(1) + assertThat(subject.lintWithContext(env, code)).isEmpty() } @Test - fun `does not report if the function has 3 or more arguments and it's defined in java - #4288`() { + fun `does not report for put functions without caller`() { val code = """ - import com.example.fromjava.Rect - - fun foo() { - val rect = Rect() - rect.set(0, 1, 2) + fun put() { } + fun f() { + put() } """ - assertThat(subject.lintWithContext(customEnv, code)).isEmpty() + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() } } } + + @Nested + @KotlinCoreEnvironmentTest(additionalJavaSourcePaths = ["java"]) + inner class WithAdditionalJavaSources(val env: KotlinCoreEnvironment) { + + @Test + fun `reports setter from java with 2 or less parameters`() { + // this test case ensures that the test environment are set up correctly. + val code = """ + import com.example.fromjava.Rect + + fun foo() { + val rect = Rect() + rect.set(0, 1) + } + """ + assertThat(subject.lintWithContext(env, code)).hasSize(1) + } + + @Test + fun `does not report if the function has 3 or more arguments and it's defined in java - #4288`() { + val code = """ + import com.example.fromjava.Rect + + fun foo() { + val rect = Rect() + rect.set(0, 1, 2) + } + """ + assertThat(subject.lintWithContext(env, code)).isEmpty() + } + } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ObjectLiteralToLambdaSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ObjectLiteralToLambdaSpec.kt index 728f977d82e..7452591d296 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ObjectLiteralToLambdaSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ObjectLiteralToLambdaSpec.kt @@ -1,7 +1,5 @@ package io.gitlab.arturbosch.detekt.rules.style -import io.github.detekt.test.utils.createEnvironment -import io.github.detekt.test.utils.resourceAsPath import io.gitlab.arturbosch.detekt.api.SourceLocation import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest import io.gitlab.arturbosch.detekt.test.assert @@ -9,591 +7,586 @@ import io.gitlab.arturbosch.detekt.test.compileAndLint import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext import io.gitlab.arturbosch.detekt.test.lintWithContext import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test -@KotlinCoreEnvironmentTest -class ObjectLiteralToLambdaSpec(val env: KotlinCoreEnvironment) { +class ObjectLiteralToLambdaSpec { val subject = ObjectLiteralToLambda() @Nested - inner class `report convertible expression` { - @Test - fun `is property`() { - val code = """ - fun interface Sam { - fun foo() - } - val a = object : Sam { - override fun foo() { - } - } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(4, 9)) - } + @KotlinCoreEnvironmentTest + inner class WithDefaultSources(val env: KotlinCoreEnvironment) { - @Test - fun `is in function`() { - val code = """ - fun interface Sam { - fun foo() - } - fun bar() { - object : Sam { - override fun foo() { + @Nested + inner class `report convertible expression` { + @Test + fun `is property`() { + val code = """ + fun interface Sam { + fun foo() } - } - } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(5, 5)) - } - - @Test - fun `is in init`() { - val code = """ - fun interface Sam { - fun foo() - } - object B { - init { - object : Sam { + val a = object : Sam { override fun foo() { } - } - } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(4, 9)) } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(6, 9)) - } - @Test - fun `is generic`() { - val code = """ - fun interface Sam { - fun foo(): T + @Test + fun `is in function`() { + val code = """ + fun interface Sam { + fun foo() + } + fun bar() { + object : Sam { + override fun foo() { + } + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(5, 5)) } - val a = object : Sam { - override fun foo(): Int { - return 1 - } - } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(4, 9)) - } - @Test - fun `has other default method`() { - val code = """ - fun interface Sam { - fun foo() - fun bar() {} + @Test + fun `is in init`() { + val code = """ + fun interface Sam { + fun foo() + } + object B { + init { + object : Sam { + override fun foo() { + } + } + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(6, 9)) } - val a = object : Sam { - override fun foo() { - } - } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(5, 9)) - } - @Test - fun `nested declaration`() { - val code = """ - interface First { - fun foo() + @Test + fun `is generic`() { + val code = """ + fun interface Sam { + fun foo(): T + } + val a = object : Sam { + override fun foo(): Int { + return 1 + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(4, 9)) } - fun interface Second: First - fun bar() { - object : Second { - override fun foo(){ + @Test + fun `has other default method`() { + val code = """ + fun interface Sam { + fun foo() + fun bar() {} } - } + val a = object : Sam { + override fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(5, 9)) } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(7, 5)) - } - @Test - fun `expression body syntax`() { - val code = """ - fun interface Sam { - fun foo(): Int - } - val a = object : Sam { - override fun foo() = 3 + @Test + fun `nested declaration`() { + val code = """ + interface First { + fun foo() + } + fun interface Second: First + + fun bar() { + object : Second { + override fun foo(){ + } + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(7, 5)) } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(4, 9)) - } - } - @Nested - inner class `is not correct implement` { - @Test - fun `without type resolution`() { - val code = """ - fun interface Sam { - fun foo() - } - val a = object : Sam { - override fun foo() { - } + @Test + fun `expression body syntax`() { + val code = """ + fun interface Sam { + fun foo(): Int + } + val a = object : Sam { + override fun foo() = 3 + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(4, 9)) } - """ - subject.compileAndLint(code).assert().isEmpty() - } - - @Test - fun `is empty interface`() { - val code = """ - interface Sam - val a = object : Sam {} - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() } - @Test - fun `is empty interface and has own function`() { - val code = """ - interface Sam - val a = object : Sam { - fun foo() { - } + @Nested + inner class `is not correct implement` { + @Test + fun `without type resolution`() { + val code = """ + fun interface Sam { + fun foo() + } + val a = object : Sam { + override fun foo() { + } + } + """ + subject.compileAndLint(code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `is single property interface`() { - val code = """ - interface Sam { - val foo: Int - } - val a = object : Sam { - override val foo = 1 + @Test + fun `is empty interface`() { + val code = """ + interface Sam + val a = object : Sam {} + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `is empty interface and has own property`() { - val code = """ - interface Sam - val a = object : Sam { - val b = 1 + @Test + fun `is empty interface and has own function`() { + val code = """ + interface Sam + val a = object : Sam { + fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `is not fun interface`() { - val code = """ - interface Sam { - fun foo() - } - val a = object : Sam { - override fun foo() { - } + @Test + fun `is single property interface`() { + val code = """ + interface Sam { + val foo: Int + } + val a = object : Sam { + override val foo = 1 + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `is not interface`() { - val code = """ - abstract class Something { - abstract fun foo() - } - val a = object : Something() { - override fun foo() { - } + @Test + fun `is empty interface and has own property`() { + val code = """ + interface Sam + val a = object : Sam { + val b = 1 + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `has multi implement`() { - val code = """ - fun interface First { - fun foo() + @Test + fun `is not fun interface`() { + val code = """ + interface Sam { + fun foo() + } + val a = object : Sam { + override fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - interface Second - val a: First = object : First, Second { - override fun foo() { - } + @Test + fun `is not interface`() { + val code = """ + abstract class Something { + abstract fun foo() + } + val a = object : Something() { + override fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `has complex implement`() { - val code = """ - abstract class First { - abstract fun foo() - } - fun interface Second { - fun foo() + @Test + fun `has multi implement`() { + val code = """ + fun interface First { + fun foo() + } + interface Second + + val a: First = object : First, Second { + override fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - val a: First = object : First(), Second { - override fun foo() { - } + @Test + fun `has complex implement`() { + val code = """ + abstract class First { + abstract fun foo() + } + fun interface Second { + fun foo() + } + + val a: First = object : First(), Second { + override fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() } - } - @Nested - inner class `has impurities` { - @Test - fun `has more than one method`() { - val code = """ - fun interface Sam { - fun foo() - } - val a = object : Sam { - override fun foo() { - } - fun bar() { - } + @Nested + inner class `has impurities` { + @Test + fun `has more than one method`() { + val code = """ + fun interface Sam { + fun foo() + } + val a = object : Sam { + override fun foo() { + } + fun bar() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `has property`() { - val code = """ - fun interface Sam { - fun foo() - } - val a = object : Sam { - private var bar = 0 - override fun foo() { - } + @Test + fun `has property`() { + val code = """ + fun interface Sam { + fun foo() + } + val a = object : Sam { + private var bar = 0 + override fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `has init`() { - val code = """ - fun interface Sam { - fun foo() - } - val a = object : Sam { - init { - } - override fun foo() { - } + @Test + fun `has init`() { + val code = """ + fun interface Sam { + fun foo() + } + val a = object : Sam { + init { + } + override fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() } - } - @Nested - inner class `java interface` { - @Test - fun `is convertible`() { - val code = """ - val a = object : Runnable { - override fun run(){ - } + @Nested + inner class `java interface` { + @Test + fun `is convertible`() { + val code = """ + val a = object : Runnable { + override fun run(){ + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(1, 9)) } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(1, 9)) - } - @Test - fun `is convertible Callable generic`() { - val code = """ - import java.util.concurrent.Callable - val a = object : Callable { - override fun call(): Int { - return 1 - } + @Test + fun `is convertible Callable generic`() { + val code = """ + import java.util.concurrent.Callable + val a = object : Callable { + override fun call(): Int { + return 1 + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(2, 9)) } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(2, 9)) - } - @Test - fun `empty interface`() { - val code = """ - import java.util.EventListener - val a = object : EventListener { - fun foo() { - } + @Test + fun `empty interface`() { + val code = """ + import java.util.EventListener + val a = object : EventListener { + fun foo() { + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `is convertible Enumeration generic`() { - val code = """ - import java.util.Enumeration - val a = object : Enumeration { - override fun hasMoreElements(): Boolean { - return true - } - - override fun nextElement(): Int { - return 1 - } + @Test + fun `is convertible Enumeration generic`() { + val code = """ + import java.util.Enumeration + val a = object : Enumeration { + override fun hasMoreElements(): Boolean { + return true + } + + override fun nextElement(): Int { + return 1 + } + } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() } @Nested - inner class JavaSourceTests { - - private val environmentWrapper = - createEnvironment(additionalJavaSourceRootPaths = listOf(resourceAsPath("java").toFile())) - private val customEnv = environmentWrapper.env - - @AfterAll - fun disposeEnvironment() { - environmentWrapper.dispose() - } - + inner class `object use itself` { @Test - fun `has other default methods`() { + fun `call 'this'`() { val code = """ - import com.example.fromjava.SamWithDefaultMethods + fun interface Sam { + fun foo() + } - fun main() { - val x = object : SamWithDefaultMethods { - override fun foo() { - println() + fun aa() { + object : Sam { + override fun foo() { + this + } } } - } """ - - subject.lintWithContext(customEnv, code).assert().hasSize(1) + subject.compileAndLintWithContext(env, code).assert().isEmpty() } @Test - fun `has only default methods`() { + fun `use 'this'`() { val code = """ - import com.example.fromjava.OnlyDefaultMethods + fun interface Sam { + fun foo() + } + + fun Sam.bar() {} - fun main() { - val x = object : OnlyDefaultMethods { + fun aa() { + object : Sam { + override fun foo() { + bar() + } + } } - } """ - subject.lintWithContext(customEnv, code).assert().isEmpty() + subject.compileAndLintWithContext(env, code).assert().isEmpty() } @Test - fun `implements a default method`() { + fun `use class method`() { val code = """ - import com.example.fromjava.OnlyDefaultMethods + fun interface Sam { + fun foo() + } - fun main() { - val x = object : OnlyDefaultMethods { - override fun foo() { - println() + fun aa() { + object : Sam { + override fun foo() { + hashCode() + } } } - } """ - subject.lintWithContext(customEnv, code).assert().isEmpty() + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - } - } - @Nested - inner class `object use itself` { - @Test - fun `call 'this'`() { - val code = """ - fun interface Sam { - fun foo() - } - - fun aa() { - object : Sam { - override fun foo() { - this + @Test + fun `call 'this' inside nested object`() { + val code = """ + fun interface Sam { + fun foo() } - } - } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - - @Test - fun `use 'this'`() { - val code = """ - fun interface Sam { - fun foo() + + fun aa() { + object : Sam { + override fun foo() { + object : Sam { + override fun foo() { + this + } + } + } + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(6, 5)) } - fun Sam.bar() {} - - fun aa() { - object : Sam { - override fun foo() { - bar() + @Test + fun `call labeled 'this'`() { + val code = """ + fun interface Sam { + fun foo() } - } + + class Target { + init { + object : Sam { + override fun foo() { + this@Target + } + } + } + } + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(7, 9)) } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() - } - @Test - fun `use class method`() { - val code = """ - fun interface Sam { - fun foo() - } - - fun aa() { - object : Sam { - override fun foo() { - hashCode() + @Test + fun `recursive call`() { + val code = """ + fun interface Sam { + fun foo() + } + + fun a() { + object : Sam { + override fun foo() { + foo() + } + } } - } + """ + subject.compileAndLintWithContext(env, code).assert().isEmpty() } - """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() } - @Test - fun `call 'this' inside nested object`() { - val code = """ - fun interface Sam { - fun foo() - } + @Nested + inner class `Edge case` { + // https://github.com/detekt/detekt/pull/3599#issuecomment-806389701 + @Test + fun `Anonymous objects are always newly created, but lambdas are singletons, so they have the same reference`() { + val code = """ + fun interface Sam { + fun foo() + } - fun aa() { - object : Sam { - override fun foo() { - object : Sam { - override fun foo() { - this - } + fun newObject() = object : Sam { + override fun foo() { } } - } + + fun lambda() = Sam {} + + val a = newObject() === newObject() // false + val b = lambda() === lambda() // true + """ + subject.compileAndLintWithContext(env, code) + .assert() + .hasSize(1) + .hasSourceLocations(SourceLocation(5, 19)) } - """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(6, 5)) } + } + + @Nested + @KotlinCoreEnvironmentTest(additionalJavaSourcePaths = ["java"]) + inner class WithAdditionalJavaSources(val env: KotlinCoreEnvironment) { @Test - fun `call labeled 'this'`() { + fun `has other default methods`() { val code = """ - fun interface Sam { - fun foo() - } - - class Target { - init { - object : Sam { + import com.example.fromjava.SamWithDefaultMethods + + fun main() { + val x = object : SamWithDefaultMethods { override fun foo() { - this@Target + println() } } - } - } + } """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(7, 9)) + + subject.lintWithContext(env, code).assert().hasSize(1) } @Test - fun `recursive call`() { + fun `has only default methods`() { val code = """ - fun interface Sam { - fun foo() - } - - fun a() { - object : Sam { - override fun foo() { - foo() + import com.example.fromjava.OnlyDefaultMethods + + fun main() { + val x = object : OnlyDefaultMethods { } - } - } + } """ - subject.compileAndLintWithContext(env, code).assert().isEmpty() + subject.lintWithContext(env, code).assert().isEmpty() } - } - @Nested - inner class `Edge case` { - // https://github.com/detekt/detekt/pull/3599#issuecomment-806389701 @Test - fun `Anonymous objects are always newly created, but lambdas are singletons, so they have the same reference`() { + fun `implements a default method`() { val code = """ - fun interface Sam { - fun foo() - } - - fun newObject() = object : Sam { - override fun foo() { - } - } - - fun lambda() = Sam {} - - val a = newObject() === newObject() // false - val b = lambda() === lambda() // true + import com.example.fromjava.OnlyDefaultMethods + + fun main() { + val x = object : OnlyDefaultMethods { + override fun foo() { + println() + } + } + } """ - subject.compileAndLintWithContext(env, code) - .assert() - .hasSize(1) - .hasSourceLocations(SourceLocation(5, 19)) + subject.lintWithContext(env, code).assert().isEmpty() } } } diff --git a/detekt-test-utils/api/detekt-test-utils.api b/detekt-test-utils/api/detekt-test-utils.api index c57ff6aed14..6e61e268505 100644 --- a/detekt-test-utils/api/detekt-test-utils.api +++ b/detekt-test-utils/api/detekt-test-utils.api @@ -47,6 +47,7 @@ public final class io/github/detekt/test/utils/StringPrintStream : java/io/Print } public abstract interface annotation class io/gitlab/arturbosch/detekt/rules/KotlinCoreEnvironmentTest : java/lang/annotation/Annotation { + public abstract fun additionalJavaSourcePaths ()[Ljava/lang/String; } public final class io/gitlab/arturbosch/detekt/rules/KotlinEnvironmentTestSetupKt { diff --git a/detekt-test-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/KotlinEnvironmentTestSetup.kt b/detekt-test-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/KotlinEnvironmentTestSetup.kt index 7daf4b41b84..97f59ef5e36 100644 --- a/detekt-test-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/KotlinEnvironmentTestSetup.kt +++ b/detekt-test-utils/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/KotlinEnvironmentTestSetup.kt @@ -2,6 +2,7 @@ package io.gitlab.arturbosch.detekt.rules import io.github.detekt.test.utils.KotlinCoreEnvironmentWrapper import io.github.detekt.test.utils.createEnvironment +import io.github.detekt.test.utils.resourceAsPath import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.ExtensionContext @@ -9,6 +10,7 @@ import org.junit.jupiter.api.extension.ParameterContext import org.junit.jupiter.api.extension.ParameterResolver import org.spekframework.spek2.dsl.Root import org.spekframework.spek2.lifecycle.CachingMode +import java.io.File import java.nio.file.Path @Deprecated( @@ -30,7 +32,9 @@ fun Root.setupKotlinEnvironment(additionalJavaSourceRootPath: Path? = null) { @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS) @ExtendWith(KotlinEnvironmentResolver::class) -annotation class KotlinCoreEnvironmentTest +annotation class KotlinCoreEnvironmentTest( + val additionalJavaSourcePaths: Array = [] +) internal class KotlinEnvironmentResolver : ParameterResolver { private var ExtensionContext.wrapper: CloseableWrapper? @@ -43,13 +47,20 @@ internal class KotlinEnvironmentResolver : ParameterResolver { override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Any { val closeableWrapper = extensionContext.wrapper - ?: CloseableWrapper(createEnvironment()).also { extensionContext.wrapper = it } + ?: CloseableWrapper( + createEnvironment(additionalJavaSourceRootPaths = extensionContext.additionalJavaSourcePaths()) + ).also { extensionContext.wrapper = it } return closeableWrapper.wrapper.env } companion object { private val NAMESPACE = ExtensionContext.Namespace.create("KotlinCoreEnvironment") private const val WRAPPER_KEY = "wrapper" + private fun ExtensionContext.additionalJavaSourcePaths(): List { + val annotation = requiredTestClass.annotations + .find { it is KotlinCoreEnvironmentTest } as? KotlinCoreEnvironmentTest ?: return emptyList() + return annotation.additionalJavaSourcePaths.map { resourceAsPath(it).toFile() } + } } private class CloseableWrapper(val wrapper: KotlinCoreEnvironmentWrapper) :