[kotlin] Run Kotlin JVM tests on both K1 and K2 frontend

#IDEA-354810 Fixed


(cherry picked from commit d29d3256472dfe368161335732ded20ae95cf34e)

IJ-MR-140910

GitOrigin-RevId: 0a9b3cae7aab473f732012ad91b4e67f97ff8697
This commit is contained in:
Bart van Helvert
2024-07-25 00:03:40 +02:00
committed by intellij-monorepo-bot
parent db79e0d621
commit 72b748d5e0
290 changed files with 1604 additions and 844 deletions

View File

@@ -0,0 +1,109 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.tests.kotlin;
import com.intellij.codeInspection.blockingCallsDetection.BlockingMethodInNonBlockingContextInspection;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider;
import static com.intellij.codeInspection.blockingCallsDetection.BlockingMethodInNonBlockingContextInspection.DEFAULT_BLOCKING_ANNOTATIONS;
import static com.intellij.codeInspection.blockingCallsDetection.BlockingMethodInNonBlockingContextInspection.DEFAULT_NONBLOCKING_ANNOTATIONS;
public abstract class KotlinBlockingCallDetectionTest extends JavaCodeInsightFixtureTestCase implements KotlinPluginModeProvider {
@Override
protected void setUp() throws Exception {
super.setUp();
BlockingMethodInNonBlockingContextInspection myInspection = new BlockingMethodInNonBlockingContextInspection();
myInspection.myBlockingAnnotations = DEFAULT_BLOCKING_ANNOTATIONS;
myInspection.myNonBlockingAnnotations = DEFAULT_NONBLOCKING_ANNOTATIONS;
myFixture.enableInspections(myInspection);
}
public void testKotlinAnnotationDetection() {
myFixture.addClass("package org.jetbrains.annotations;\n" +
"public @interface Blocking {}");
myFixture.addClass("package org.jetbrains.annotations;\n" +
"public @interface NonBlocking {}");
myFixture.addFileToProject("/TestKotlinAnnotationDetection.kt",
"""
import org.jetbrains.annotations.Blocking
import org.jetbrains.annotations.NonBlocking
@NonBlocking
fun nonBlockingFunction() {
<warning descr="Possibly blocking call in non-blocking context could lead to thread starvation">blockingFunction</warning>();
}
@Blocking
fun blockingFunction() {}""");
myFixture.testHighlighting(true, false, true, "TestKotlinAnnotationDetection.kt");
}
public void testKotlinThrowsTypeDetection() {
myFixture.addClass("package org.jetbrains.annotations;\n" +
"public @interface NonBlocking {}");
myFixture.configureByText("/TestKotlinThrowsTypeDetection.kt",
"""
import org.jetbrains.annotations.NonBlocking
import java.net.URL
@NonBlocking
fun nonBlockingFunction() {
Thread.<warning descr="Possibly blocking call in non-blocking context could lead to thread starvation">sleep</warning>(111);
\s
URL("https://example.com")
}""");
myFixture.checkHighlighting(true, false, true);
}
public void testDelegatingConstructor() {
myFixture.addClass("package org.jetbrains.annotations;\n" +
"public @interface Blocking {}");
myFixture.addClass("package org.jetbrains.annotations;\n" +
"public @interface NonBlocking {}");
myFixture.addClass("""
import org.jetbrains.annotations.*;
public class BlockingCtrClass {
@Blocking
BlockingCtrClass() {
}
public static class Intermediate extends BlockingCtrClass {}
}
"""
);
myFixture.configureByText("file.kt",
"""
import org.jetbrains.annotations.*
class NonBlockingCtrClass : BlockingCtrClass {
@NonBlocking
<warning descr="Possibly blocking call from implicit constructor call in non-blocking context could lead to thread starvation">constructor</warning>() {}
}
class NonBlockingCtrClassWithIntermediate : BlockingCtrClass.Intermediate {
@NonBlocking
<warning descr="Possibly blocking call from implicit constructor call in non-blocking context could lead to thread starvation">constructor</warning>() {}
}
class NonBlockingCtrClassExplicit @NonBlocking constructor() : <warning descr="Possibly blocking call in non-blocking context could lead to thread starvation">BlockingCtrClass</warning>()
class NonBlockingCtrClassExplicit2 : BlockingCtrClass {
@NonBlocking
constructor() : <warning descr="Possibly blocking call in non-blocking context could lead to thread starvation">super</warning>()
}
open class KotlinBlockingCtr @Blocking constructor()
class NonBlockingCtr : KotlinBlockingCtr {
@NonBlocking
<warning descr="Possibly blocking call from implicit constructor call in non-blocking context could lead to thread starvation">constructor</warning>() {}
}
""");
myFixture.testHighlighting(true, false, true, "file.kt");
}
}

View File

@@ -0,0 +1,56 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.CallMatcherTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import com.siyeh.ig.callMatcher.CallMatcher
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinCallMatcherTest : CallMatcherTestBase(), KotlinPluginModeProvider {
fun testInstanceMethodCall() {
checkMatchCall(JvmLanguage.KOTLIN, CallMatcher.instanceCall("Foo", "bar").parameterCount(0), """
class Foo { fun bar() { } }
fun main() { Foo().bar() }
""".trimIndent())
}
fun testMultipleArgumentsCall() {
checkMatchCall(JvmLanguage.KOTLIN, CallMatcher.instanceCall("Foo", "bar").parameterCount(3), """
class Foo { fun bar(x: Int, y: Int, z: Int) { } }
fun main() { Foo().bar(0, 0, 0) }
""".trimIndent())
}
fun testMultipleArgumentsCallDefaultArg() {
checkMatchCall(JvmLanguage.KOTLIN, CallMatcher.instanceCall("Foo", "bar").parameterCount(3), """
class Foo { fun bar(x: Int, y: Int, z: Int = 0) { } }
fun main() { Foo().bar(0, 0) }
""".trimIndent())
}
fun testMultipleArgumentTypes() {
checkMatchCall(JvmLanguage.KOTLIN, CallMatcher.instanceCall("Foo", "bar").parameterTypes("int", "long", "double"), """
class Foo { fun bar(x: Int, y: Long, z: Double) { } }
fun main() { Foo().bar(0, 0L, 0.0) }
""".trimIndent())
}
fun testInstanceMethodReference() {
checkMatchCallableReference(JvmLanguage.KOTLIN, CallMatcher.instanceCall("java.lang.String", "plus").parameterCount(1), """
class Foo { fun bar(arg: String.(String) -> String) { } }
class Main { fun main() { Foo().bar(String::plus) } }
""".trimIndent())
}
fun testMethodReferenceArgumentTypes() {
checkMatchCallableReference(JvmLanguage.KOTLIN, CallMatcher.instanceCall("java.lang.String", "plus").parameterTypes("java.lang.Object"), """
class Foo { fun bar(arg: String.(String) -> String) { } }
class Main { fun main() { Foo().bar(String::plus) } }
""".trimIndent())
}
}

View File

@@ -0,0 +1,74 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.DependencyInspectionTestBase
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinDependencyInspectionTest : DependencyInspectionTestBase(), KotlinPluginModeProvider {
fun `test illegal imported dependency Java API`() = dependencyViolationTest(javaFooFile, "ImportClientJava.kt", """
package pkg.client
import <error descr="Dependency rule 'Deny usages of scope 'JavaFoo' in scope 'ImportClientJava'.' is violated">pkg.api.JavaFoo</error>
fun main() {
<error descr="Dependency rule 'Deny usages of scope 'JavaFoo' in scope 'ImportClientJava'.' is violated">JavaFoo()</error>
}
""".trimIndent())
fun `test illegal imported dependency Kotlin API`() = dependencyViolationTest(kotlinFooFile, "ImportClientKotlin.kt", """
package pkg.client
import <error descr="Dependency rule 'Deny usages of scope 'KotlinFoo' in scope 'ImportClientKotlin'.' is violated">pkg.api.KotlinFoo</error>
fun main() {
<error descr="Dependency rule 'Deny usages of scope 'KotlinFoo' in scope 'ImportClientKotlin'.' is violated">KotlinFoo()</error>
}
""".trimIndent())
fun `test illegal imported dependency skip imports`() = dependencyViolationTest(kotlinFooFile, "ImportClientKotlin.kt", """
package pkg.client
import pkg.api.KotlinFoo
fun main() {
<error descr="Dependency rule 'Deny usages of scope 'KotlinFoo' in scope 'ImportClientKotlin'.' is violated">KotlinFoo()</error>
}
""".trimIndent(), skipImports = true)
fun `test illegal imported dependency Kotlin API in Java`() = dependencyViolationTest(kotlinFooFile, "ImportClientKotlin.java", """
package pkg.client;
import <error descr="Dependency rule 'Deny usages of scope 'KotlinFoo' in scope 'ImportClientKotlin'.' is violated">pkg.api.KotlinFoo</error>;
class Client {
public static void main(String[] args) {
new <error descr="Dependency rule 'Deny usages of scope 'KotlinFoo' in scope 'ImportClientKotlin'.' is violated">KotlinFoo</error>();
}
}
""".trimIndent())
fun `test illegal fully qualified dependency Java API`() = dependencyViolationTest(javaFooFile, "FqClientJava.kt", """
package pkg.client
fun main() {
<error descr="Dependency rule 'Deny usages of scope 'JavaFoo' in scope 'FqClientJava'.' is violated">pkg.api.JavaFoo()</error>
}
""".trimIndent())
fun `test illegal fully qualified dependency Kotlin API`() = dependencyViolationTest(kotlinFooFile, "FqClientKotlin.kt", """
package pkg.client
fun main() {
<error descr="Dependency rule 'Deny usages of scope 'KotlinFoo' in scope 'FqClientKotlin'.' is violated">pkg.api.KotlinFoo()</error>
}
""".trimIndent())
fun `test illegal fully qualified dependency Kotlin API in Java`() = dependencyViolationTest(kotlinFooFile, "FqClientKotlin.java", """
package pkg.client;
class Client {
public static void main(String[] args) {
new <error descr="Dependency rule 'Deny usages of scope 'KotlinFoo' in scope 'FqClientKotlin'.' is violated">pkg.api.KotlinFoo</error>();
}
}
""".trimIndent())
}

View File

@@ -0,0 +1,21 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.codeInspection.emptyMethod.EmptyMethodInspection
import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.jvm.analysis.testFramework.JvmInspectionTestBase
import com.intellij.testFramework.TestDataPath
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
private const val inspectionPath = "/codeInspection/emptyMethod"
@TestDataPath("\$CONTENT_ROOT/testData$inspectionPath")
abstract class KotlinEmptyMethodInspectionTest : JvmInspectionTestBase(), KotlinPluginModeProvider {
override var inspection = EmptyMethodInspection()
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + inspectionPath
fun `test basic`() {
myFixture.testInspection("basic", GlobalInspectionToolWrapper(inspection))
}
}

View File

@@ -0,0 +1,191 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.JavaApiUsageInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import com.intellij.pom.java.LanguageLevel
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinJavaApiUsageInspectionTest : JavaApiUsageInspectionTestBase(), KotlinPluginModeProvider {
fun `test constructor`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_4)
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
fun foo() {
throw <error descr="Usage of API documented as @since 1.5+">IllegalArgumentException</error>("", RuntimeException());
}
""".trimIndent())
}
fun `test ignored`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
myFixture.addClass("""
package java.awt.geom;
public class GeneralPath {
public void moveTo(int x, int y) { }
}
""".trimIndent())
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.awt.geom.GeneralPath
fun foo() {
val path = GeneralPath()
path.moveTo(0, 0)
}
""".trimIndent())
}
fun `test qualified reference`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.nio.charset.StandardCharsets
fun main() {
<error descr="Usage of API documented as @since 1.7+">StandardCharsets</error>.UTF_8
}
""".trimIndent())
}
fun `test reference in callable reference`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
val withErrorMessage = "\"default charset \${<error descr=\"Usage of API documented as @since 1.7+\">StandardCharsets</error>.UTF_8}\"::toString"
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.nio.charset.StandardCharsets
fun main() {
${withErrorMessage}
}
""".trimIndent())
""::toString
}
fun `test annotation`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
@file:Suppress("UNUSED_PARAMETER")
class Annotation {
@<error descr="Usage of API documented as @since 1.7+">SafeVarargs</error>
fun foo(vararg ls: List<String>) { }
}
""".trimIndent())
}
fun `test override annotation`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
import java.util.Map
abstract class OverrideAnnotation : Map<String, String> {
override fun <error descr="Usage of API documented as @since 1.8+">getOrDefault</error>(key: Any?, defaultValue: String?): String {
return ""
}
}
""".trimIndent())
}
fun `test default methods`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNUSED_VARIABLE")
import java.util.Iterator
class <error descr="Default method 'remove' is not overridden. It would cause compilation problems with JDK 6">DefaultMethods</error> : Iterator<String> {
override fun hasNext(): Boolean {
return false
}
override fun next(): String {
return ""
}
class T : Iterator<String> {
override fun hasNext(): Boolean {
return false
}
override fun next(): String {
return ""
}
override fun remove() { }
}
init {
val it = <error descr="Default method 'remove' is not overridden. It would cause compilation problems with JDK 6">object</error> : Iterator<String> {
override fun hasNext(): Boolean {
return false
}
override fun next(): String {
return ""
}
}
}
}
""".trimIndent())
}
fun `test single method multiple overrides`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class CustomList : java.util.AbstractList<Int>() {
override val size: Int = 0
override fun get(index: Int): Int = 0
override fun <error descr="Usage of API documented as @since 1.8+">spliterator</error>(): java.util.<error descr="Usage of API documented as @since 1.8+">Spliterator</error><Int> =
java.util.<error descr="Usage of API documented as @since 1.8+">Spliterators</error>.spliterator(this, 0)
}
""".trimIndent())
}
fun `test raw inherit from newly generified`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
myFixture.addClass("""
package javax.swing;
public class AbstractListModel<K> {}
""".trimIndent())
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class RawInheritFromNewlyGenerified {
private lateinit var myModel: AbstractCCM<String>
}
abstract class AbstractCCM<T> : javax.swing.AbstractListModel<String>() { }
""".trimIndent())
}
fun `test generified`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_6)
myFixture.addClass("""
package javax.swing;
public interface ListModel<E> { }
""".trimIndent())
myFixture.addClass("""
package javax.swing;
public class AbstractListModel<K> implements ListModel<E> { }
""".trimIndent())
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import javax.swing.AbstractListModel
abstract class AbstractCCM<T> : <error descr="Usage of generified after 1.6 API which would cause compilation problems with JDK 6">AbstractListModel</error><T>() { }
""".trimIndent())
}
fun `test no highlighting in kdoc`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_1_7)
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class Javadoc {
/**
* [java.util.function.Predicate]
*/
fun test() {
return
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,24 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.codeInspection.deprecation.MarkedForRemovalInspection
import com.intellij.jvm.analysis.testFramework.JvmInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinMarkedForRemovalInspectionTest : JvmInspectionTestBase(), KotlinPluginModeProvider {
fun `test highlighted as deprecated for removal`() {
myFixture.addClass("""
package test;
@Deprecated(forRemoval = true)
class MyTest {}
""".trimIndent())
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
package test
fun main() {
<error descr="'test.MyTest' is deprecated and marked for removal"><warning descr="[DEPRECATION] 'MyTest' is deprecated. Deprecated in Java">MyTest</warning></error>()
}
""".trimIndent())
}
override val inspection = MarkedForRemovalInspection()
}

View File

@@ -0,0 +1,242 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.MigrationTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import com.intellij.refactoring.migration.MigrationMapEntry
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinMigrationTest : MigrationTestBase(), KotlinPluginModeProvider {
fun `test package`() {
migrationTest(JvmLanguage.KOTLIN, before = """
package p1
import qqq.AAA
class C {
val a = AAA()
}
""".trimIndent(), after = """
package p1
import jetbrains.test.AAA
class C {
val a = AAA()
}
""".trimIndent(), MigrationMapEntry("qqq", "jetbrains.test", MigrationMapEntry.PACKAGE, true)
)
migrationTest(JvmLanguage.KOTLIN, before = """
package p1
import qqq.*
class C {
val a = qqq.AAA()
}
""".trimIndent(), after = """
package p1
import jetbrains.test.*
class C {
val a = jetbrains.test.AAA()
}
""".trimIndent(), MigrationMapEntry("qqq", "jetbrains.test", MigrationMapEntry.PACKAGE, true)
)
}
fun `test package migration with non existing package`() {
migrationTest(JvmLanguage.KOTLIN, before = """
package p1
import qqq.AAA
class C {
val a = AAA()
}
""".trimIndent(), after = """
package p1
import zzz.bbb.AAA
class C {
val a = AAA()
}
""".trimIndent(), MigrationMapEntry("qqq", "zzz.bbb", MigrationMapEntry.PACKAGE, true)
)
migrationTest(JvmLanguage.KOTLIN, before = """
package p1
import qqq.*
class C {
val a = qqq.AAA()
}
""".trimIndent(), after = """
package p1
import zzz.bbb.*
class C {
val a = zzz.bbb.AAA()
}
""".trimIndent(), MigrationMapEntry("qqq", "zzz.bbb", MigrationMapEntry.PACKAGE, true)
)
}
fun `test two classes`() {
migrationTest(JvmLanguage.KOTLIN, before = """
class A {}
class A1 {}
class B {}
class B1 {}
public class Test {
val a: A
val b: B
}
""".trimIndent(), after = """
class A {}
class A1 {}
class B {}
class B1 {}
public class Test {
val a: A1
val b: B1
}
""".trimIndent(),
MigrationMapEntry("A", "A1", MigrationMapEntry.CLASS, true),
MigrationMapEntry("B", "B1", MigrationMapEntry.CLASS, true)
)
}
fun `test two non existent classes`() {
migrationTest(JvmLanguage.KOTLIN, before = """
package p1
import qqq.aaa.XXX
class C {
val you = XXX()
}
""".trimIndent(), after = """
package p1
import zzz.bbb.QQQ
class C {
val you = QQQ()
}
""".trimIndent(), MigrationMapEntry("qqq.aaa.XXX", "zzz.bbb.QQQ", MigrationMapEntry.CLASS, false)
)
migrationTest(JvmLanguage.KOTLIN, before = """
package p1
import qqq.aaa.*
class C1 {
val you = XXX()
}
""".trimIndent(), after = """
package p1
import qqq.aaa.*
import zzz.bbb.QQQ
class C1 {
val you = QQQ()
}
""".trimIndent(), MigrationMapEntry("qqq.aaa.XXX", "zzz.bbb.QQQ", MigrationMapEntry.CLASS, false)
)
}
fun `test non existing class and non existing package`() {
migrationTest(JvmLanguage.KOTLIN, before = """
package p1
import qqq.aaa.XXX
class C {
val you = XXX()
}
""".trimIndent(), after = """
package p1
import java.lang.String
class C {
val you = String()
}
""".trimIndent(), MigrationMapEntry("qqq.aaa.XXX", "java.lang.String", MigrationMapEntry.CLASS, false)
)
migrationTest(JvmLanguage.KOTLIN, before = """
package p1
import qqq.aaa.*
class C1 {
val you = XXX()
}
""".trimIndent(), after = """
package p1
import java.lang.String
class C1 {
val you = String()
}
""".trimIndent(), MigrationMapEntry("qqq.aaa.XXX", "java.lang.String", MigrationMapEntry.CLASS, false)
)
}
fun `test same short name class`() {
migrationTest(JvmLanguage.KOTLIN, before = """
import aaa.*
public class C {
@Test
fun foo() { }
@Test
fun bar() { }
}
""".trimIndent(), after = """
import aaa.*
import bbb.Test
public class C {
@Test
fun foo() { }
@Test
fun bar() { }
}
""".trimIndent(), MigrationMapEntry("aaa.Test", "bbb.Test", MigrationMapEntry.CLASS, false)
)
migrationTest(JvmLanguage.KOTLIN, before = """
import aaa.Test
public class C1 {
@Test
fun foo() { }
@Test
fun bar() { }
}
""".trimIndent(), after = """
import bbb.Test
public class C1 {
@Test
fun foo() { }
@Test
fun bar() { }
}
""".trimIndent(), MigrationMapEntry("aaa.Test", "bbb.Test", MigrationMapEntry.CLASS, false)
)
}
}

View File

@@ -0,0 +1,17 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.jvm.analysis.internal.testFramework.MissingDeprecatedAnnotationOnScheduledForRemovalApiInspectionTestBase
import com.intellij.testFramework.TestDataPath
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
private const val inspectionPath = "/codeInspection/missingDeprecatedAnnotationOnScheduledForRemovalApi"
@TestDataPath("\$CONTENT_ROOT/testData$inspectionPath")
abstract class KotlinMissingDeprecatedAnnotationOnScheduledForRemovalApiInspectionTest : MissingDeprecatedAnnotationOnScheduledForRemovalApiInspectionTestBase(), KotlinPluginModeProvider {
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + inspectionPath
fun `test missing @Deprecated on @ScheduledForRemoval APIs`() {
myFixture.testHighlighting("missingDeprecatedAnnotations.kt")
}
}

View File

@@ -0,0 +1,44 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.MustAlreadyBeRemovedApiInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinMustAlreadyBeRemovedApiInspectionTest : MustAlreadyBeRemovedApiInspectionTestBase(), KotlinPluginModeProvider {
fun `test APIs must have been removed`() {
inspection.currentVersion = "3.0"
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.jetbrains.annotations.ApiStatus
@ApiStatus.ScheduledForRemoval(inVersion = "2.0")
@Deprecated("")
class <error descr="API must have been removed in version 2.0 but the current version is 3.0">Warnings</error> {
@ApiStatus.ScheduledForRemoval(inVersion = "2.0")
@Deprecated("")
var <error descr="API must have been removed in version 2.0 but the current version is 3.0">field</error>: String? = null
@ApiStatus.ScheduledForRemoval(inVersion = "2.0")
@Deprecated("")
fun <error descr="API must have been removed in version 2.0 but the current version is 3.0">method</error>() {
}
}
//No warnings should be produced.
@Deprecated("")
@ApiStatus.ScheduledForRemoval(inVersion = "5.0")
class NoWarnings {
@Deprecated("")
@ApiStatus.ScheduledForRemoval(inVersion = "5.0")
var field: String? = null
@Deprecated("")
@ApiStatus.ScheduledForRemoval(inVersion = "5.0")
fun method() {
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,37 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.codeInspection.NonExtendableApiUsageInspection
import com.intellij.jvm.analysis.testFramework.JvmInspectionTestBase
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ContentEntry
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.TestDataPath
import com.intellij.util.PathUtil
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
@TestDataPath("/testData/codeInspection/nonExtendableApiUsage")
abstract class KotlinNonExtendableApiUsageInspectionTest : JvmInspectionTestBase(), KotlinPluginModeProvider {
override val inspection = NonExtendableApiUsageInspection()
override fun getProjectDescriptor() = object : ProjectDescriptor(LanguageLevel.HIGHEST) {
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
super.configureModule(module, model, contentEntry)
PsiTestUtil.addProjectLibrary(model, "annotations", listOf(PathUtil.getJarPathForClass(ApiStatus.NonExtendable::class.java)))
PsiTestUtil.addProjectLibrary(model, "library", listOf(testDataPath))
}
}
override fun getBasePath() = "/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/nonExtendableApiUsage"
fun `test java extensions`() {
myFixture.testHighlighting("plugin/javaExtensions.java")
}
fun `test kotlin extensions`() {
myFixture.allowTreeAccessForAllFiles()
myFixture.testHighlighting("plugin/kotlinExtensions.kt")
}
}

View File

@@ -0,0 +1,67 @@
package com.intellij.codeInspection.tests.kotlin;
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil;
import com.intellij.openapi.application.PathManager;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.TestDataPath;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import com.intellij.util.PathUtil;
import kotlin.KotlinVersion;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider;
import org.jetbrains.uast.ULiteralExpression;
import org.junit.Assume;
import java.io.File;
import java.util.Set;
import static com.intellij.codeInspection.NonNlsUastUtil.isNonNlsStringLiteral;
import static com.intellij.jvm.analysis.internal.testFramework.JvmAnalysisTestsUastUtil.getUElementsOfTypeFromFile;
@TestDataPath("$CONTENT_ROOT/testData/codeInspection/nonNls")
public abstract class KotlinNonNlsUastUtilTest extends JavaCodeInsightFixtureTestCase implements KotlinPluginModeProvider {
@Override
protected void setUp() throws Exception {
super.setUp();
Assume.assumeTrue(KotlinVersion.CURRENT.isAtLeast(1, 2, 60));
}
@Override
protected String getBasePath() {
return KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + "/codeInspection/nonNls";
}
@Override
protected String getTestDataPath() {
return PathManager.getCommunityHomePath().replace(File.separatorChar, '/') + getBasePath();
}
@Override
protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) {
moduleBuilder.addLibrary("annotations", PathUtil.getJarPathForClass(NonNls.class));
}
public void testNonNlsStringLiterals() {
PsiFile file = myFixture.configureByFile("NonNlsStringLiteral.kt");
Set<ULiteralExpression> expressions = getUElementsOfTypeFromFile(file, ULiteralExpression.class);
assertSize(20, expressions); // multiline string literal is processed as 4 string literals
expressions.forEach(expression -> assertTrue("\"" + expression.getSourcePsi().getText() + "\" should be a NonNls StringLiteral",
isNonNlsStringLiteral(expression)));
}
public void testPlainStringLiterals() {
PsiFile file = myFixture.configureByFile("PlainStringLiteral.kt");
Set<ULiteralExpression> expressions = getUElementsOfTypeFromFile(file, ULiteralExpression.class);
assertSize(9, expressions);
expressions.forEach(expression -> assertFalse(isNonNlsStringLiteral(expression)));
}
public void testLiteralsInNonNlsClass() {
PsiFile file = myFixture.configureByFile("LiteralsInNonNlsClass.kt");
Set<ULiteralExpression> expressions = getUElementsOfTypeFromFile(file, ULiteralExpression.class);
assertSize(8, expressions);
expressions.forEach(expression -> assertTrue(isNonNlsStringLiteral(expression)));
}
}

View File

@@ -0,0 +1,39 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.ObsoleteApiUsageInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinObsoleteApiUsageInspectionTest : ObsoleteApiUsageInspectionTestBase(), KotlinPluginModeProvider {
fun `test direct usage`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class B {
fun f(a: A) {
a.<warning descr="Obsolete API is used">f</warning>();
}
}
""".trimIndent())
}
fun `test override`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class C : A() {
override fun <warning descr="Obsolete API is used">f</warning>() { }
}
@org.jetbrains.annotations.ApiStatus.Obsolete
class D : A() {
override fun <warning descr="Obsolete API is used">f</warning>() { }
}
""".trimIndent())
}
fun `test generic reference`() {
myFixture.addClass("@org.jetbrains.annotations.ApiStatus.Obsolete public interface I<T> {}")
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class U {
fun u(i: <warning descr="Obsolete API is used">I</warning><Int>) = i
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,47 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.codeInspection.OverrideOnlyInspection
import com.intellij.jvm.analysis.testFramework.JvmInspectionTestBase
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ContentEntry
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.TestDataPath
import com.intellij.util.PathUtil
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
@TestDataPath("/testData/codeInspection/overrideOnly")
abstract class KotlinOverrideOnlyInspectionTest : JvmInspectionTestBase(), KotlinPluginModeProvider {
override val inspection: InspectionProfileEntry = OverrideOnlyInspection()
override fun getProjectDescriptor() = object : ProjectDescriptor(LanguageLevel.HIGHEST) {
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
super.configureModule(module, model, contentEntry)
PsiTestUtil.addProjectLibrary(model, "annotations", listOf(PathUtil.getJarPathForClass(ApiStatus.OverrideOnly::class.java)))
PsiTestUtil.addProjectLibrary(model, "library", listOf(testDataPath))
}
}
override fun getBasePath() = "/jvm/jvm-analysis-kotlin-tests-shared/testData/codeInspection/overrideOnly"
fun `test java invocations`() {
myFixture.testHighlighting("plugin/JavaCode.java")
}
fun `test kotlin invocations`() {
myFixture.allowTreeAccessForAllFiles()
myFixture.testHighlighting("plugin/KotlinCode.kt")
}
fun `test java delegation`() {
myFixture.testHighlighting("plugin/DelegateJavaCode.java")
}
fun `test kotlin delegation`() {
myFixture.allowTreeAccessForAllFiles()
myFixture.testHighlighting("plugin/DelegateKotlinCode.kt")
}
}

View File

@@ -0,0 +1,40 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.tests.kotlin
import com.intellij.analysis.AnalysisScope
import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper
import com.intellij.codeInspection.java19modules.Java9RedundantRequiresStatementInspection
import com.intellij.java.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase
import com.intellij.java.testFramework.fixtures.MultiModuleJava9ProjectDescriptor
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.openapi.application.PathManager
import com.intellij.testFramework.InspectionTestUtil
import com.intellij.testFramework.createGlobalContextForTool
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
import java.io.File
abstract class KotlinRedundantRequiresStatementTest : LightJava9ModulesCodeInsightFixtureTestCase(), KotlinPluginModeProvider {
fun testStdlib() {
val mainText = """
package org.example.main
class Main {
fun main() {}
}""".trimIndent()
addFile("org.example.main/Main.kt", mainText)
addFile("module-info.java", "module MAIN { requires kotlin.stdlib; }", MultiModuleJava9ProjectDescriptor.ModuleDescriptor.MAIN)
val toolWrapper = GlobalInspectionToolWrapper(Java9RedundantRequiresStatementInspection())
val scope = AnalysisScope(project)
val globalContext = createGlobalContextForTool(scope, project, listOf(toolWrapper))
InspectionTestUtil.runTool(toolWrapper, scope, globalContext)
InspectionTestUtil.compareToolResults(globalContext, toolWrapper, true, testDataPath + getTestName(true))
}
override fun getBasePath(): String {
return KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + "/codeInspection/redundantRequires/"
}
override fun getTestDataPath(): String {
return PathManager.getCommunityHomePath().replace(File.separatorChar, '/') + basePath
}
}

View File

@@ -0,0 +1,26 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.SameParameterValueInspectionTestBase
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinSameParameterValueGlobalInspectionTest : SameParameterValueInspectionTestBase(false), KotlinPluginModeProvider {
fun testEntryPoint() {
doHighlightTest(runDeadCodeFirst = true)
}
fun testMethodWithSuper() {
doHighlightTest()
}
fun testVarargs() {
doHighlightTest()
}
fun testNamedArg() {
doHighlightTest()
}
fun testNegativeDouble() {
doHighlightTest()
}
}

View File

@@ -0,0 +1,30 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.SameParameterValueInspectionTestBase
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinSameParameterValueLocalInspectionTest : SameParameterValueInspectionTestBase(true), KotlinPluginModeProvider {
fun testEntryPoint() {
doHighlightTest(runDeadCodeFirst = true)
}
fun testMethodWithSuper() {
doHighlightTest()
}
fun testVarargs() {
doHighlightTest()
}
fun testNamedArg() {
doHighlightTest()
}
fun testNegativeDouble() {
doHighlightTest()
}
fun testReceiver() {
doHighlightTest()
}
}

View File

@@ -0,0 +1,31 @@
package com.intellij.codeInspection.tests.kotlin;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.sameReturnValue.SameReturnValueInspection;
import com.intellij.testFramework.JavaInspectionTestCase;
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider;
public abstract class KotlinSameReturnValueGlobalInspectionTest extends JavaInspectionTestCase implements KotlinPluginModeProvider {
private final SameReturnValueInspection myGlobalTool = new SameReturnValueInspection();
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/inspection/jvm";
}
private String getGlobalTestDir() {
return "sameReturnValue/" + getTestName(true);
}
public void testJava() {
doTest(getGlobalTestDir(), myGlobalTool);
}
public void testKotlin() {
doTest(getGlobalTestDir(), myGlobalTool);
}
public void testMixed() {
doTest(getGlobalTestDir(), myGlobalTool);
}
}

View File

@@ -0,0 +1,28 @@
package com.intellij.codeInspection.tests.kotlin;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.sameReturnValue.SameReturnValueInspection;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider;
public abstract class KotlinSameReturnValueLocalInspectionTest extends LightJavaCodeInsightFixtureTestCase implements KotlinPluginModeProvider {
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/inspection/sameReturnValue/";
}
@Override
protected void setUp() throws Exception {
super.setUp();
myFixture.enableInspections(new SameReturnValueInspection().getSharedLocalInspectionTool());
}
public void testJava() {
myFixture.testHighlighting(getTestName(false) + ".java");
}
public void testKotlin() {
myFixture.testHighlighting(getTestName(false) + ".kt");
}
}

View File

@@ -0,0 +1,76 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.SerializableHasSerialVersionUidFieldInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.IdeaTestUtil
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinSerializableHasSerialVersionUidFieldInspectionTest : SerializableHasSerialVersionUidFieldInspectionTestBase(), KotlinPluginModeProvider {
fun `test highlighting`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.io.Serializable
class <warning descr="'Foo' does not define a 'serialVersionUID' field">Foo</warning> : Serializable { }
""".trimIndent())
}
fun `test quickfix`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_11)
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
import java.io.Serializable
class Fo<caret>o : Serializable { }
""".trimIndent(), """
import java.io.Serializable
class Foo : Serializable {
companion object {
private const val serialVersionUID: Long = 7429157667498829299L
}
}
""".trimIndent(), "Add 'const val' property 'serialVersionUID' to 'Foo'")
}
fun `test quickfix companion exists`() {
myFixture.setLanguageLevel(LanguageLevel.JDK_11)
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
import java.io.Serializable
class Fo<caret>o : Serializable {
companion object {
val bar = 0
}
}
""".trimIndent(), """
import java.io.Serializable
class Foo : Serializable {
companion object {
private const val serialVersionUID: Long = -7315889077010185135L
val bar = 0
}
}
""".trimIndent(), "Add 'const val' property 'serialVersionUID' to 'Foo'")
}
fun `test quickfix @Serial annotation`() {
IdeaTestUtil.withLevel(module, LanguageLevel.JDK_14) {
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
import java.io.Serializable
class Fo<caret>o : Serializable { }
""".trimIndent(), """
import java.io.Serial
import java.io.Serializable
class Foo : Serializable {
companion object {
@Serial
private const val serialVersionUID: Long = 7429157667498829299L
}
}
""".trimIndent(), "Add 'const val' property 'serialVersionUID' to 'Foo'")
}
}
}

View File

@@ -0,0 +1,151 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.SuppressionAnnotationInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinSuppressionAnnotationInspectionTest : SuppressionAnnotationInspectionTestBase(), KotlinPluginModeProvider {
fun `test highlighting`() {
inspection.myAllowedSuppressions.add("FreeSpeech")
myFixture.testHighlighting(
JvmLanguage.KOTLIN,
"""
@<warning descr="Annotation suppresses 'ALL' and 'SuppressionAnnotation'">Suppress</warning>("ALL", "SuppressionAnnotation")
class A {
@<warning descr="Annotation suppresses 'PublicField'">Suppress</warning>("PublicField")
var s: String? = null
@<warning descr="Annotation suppresses">Suppress</warning>
var t: String? = null
fun foo() {
<warning descr="Comment suppresses 'HardCodedStringLiteral'">//noinspection HardCodedStringLiteral</warning>
any("hello")
<warning descr="Comment suppresses">// noinspection</warning>
any()
}
@Suppress("FreeSpeech")
fun bar() {
// noinspection FreeSpeech
any()
}
}
private fun any(s: String? = null): String? = s
""".trimIndent()
)
}
fun `test quickfix - remove annotation`() {
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
class A {
@Supp<caret>ress("PublicField", "HardCodedStringLiteral")
var s: String = "test"
}
""".trimIndent(), """
class A {
var s: String = "test"
}
""".trimIndent(), "Remove '@Suppress' annotation", testPreview = true)
}
fun `test quickfix - remove comment`() {
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
class A {
//noinspection PublicField, Hard<caret>CodedStringLiteral
var s: String = "test"
}
""".trimIndent(), """
class A {
var s: String = "test"
}
""".trimIndent(), "Remove //noinspection", testPreview = true)
}
fun `test quickfix - allow a single suppression from annotation`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
class A {
@Supp<caret>ress("PublicField")
var s: String = "test"
}
""".trimIndent(), "PublicField")
}
fun `test quickfix - allow a single suppression from annotation when array form used`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
class A {
@Supp<caret>ress(["PublicField"])
var s: String = "test"
}
""".trimIndent(), "PublicField")
}
fun `test quickfix - allow a single suppression from annotation when explicit attribute name exists`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
class A {
@Supp<caret>ress(names = "PublicField")
var s: String = "test"
}
""".trimIndent(), "PublicField")
}
fun `test quickfix - allow multiple suppressions from annotation`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
class A {
@Supp<caret>ress("PublicField", "HardCodedStringLiteral")
var s: String = "test"
}
""".trimIndent(), "PublicField", "HardCodedStringLiteral")
}
fun `test quickfix - allow multiple suppressions from annotation when array form used`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
class A {
@Supp<caret>ress(["PublicField", "HardCodedStringLiteral"])
var s: String = "test"
}
""".trimIndent(), "PublicField", "HardCodedStringLiteral")
}
fun `test quickfix - allow multiple suppressions from annotation when explicit attribute name exists`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
class A {
@Supp<caret>ress(names = ["PublicField", "HardCodedStringLiteral"])
var s: String = "test"
}
""".trimIndent(), "PublicField", "HardCodedStringLiteral")
}
fun `test quickfix - allow multiple suppressions from annotation when constants used`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
object Constants {
const val PUBLIC_FIELD = "PublicField"
const val HARD_CODED_STRING_LITERAL = "HardCodedStringLiteral"
}
class A {
@Supp<caret>ress([Constants.PUBLIC_FIELD, Constants.HARD_CODED_STRING_LITERAL])
var s: String = "test"
}
""".trimIndent(), "PublicField", "HardCodedStringLiteral")
}
fun `test quickfix - allow a single suppression from comment`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
class A {
//noinspection Public<caret>Field
var s: String = "test"
}
""".trimIndent(), "PublicField")
}
fun `test quickfix - allow multiple suppressions from comment`() {
testAllowSuppressionQuickFix(JvmLanguage.KOTLIN, """
class A {
//noinspection Public<caret>Field, Hard<caret>CodedStringLiteral
var s: String = "test"
}
""".trimIndent(), "PublicField", "HardCodedStringLiteral")
}
}

View File

@@ -0,0 +1,27 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.testFramework.TestDataPath
import com.siyeh.ig.dependency.SuspiciousPackagePrivateAccessInspectionTestCase
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
@TestDataPath("\$CONTENT_ROOT/testData/codeInspection/suspiciousPackagePrivateAccess")
abstract class KotlinSuspiciousPackagePrivateAccessInspectionTest : SuspiciousPackagePrivateAccessInspectionTestCase("kt"), KotlinPluginModeProvider {
fun testAccessingPackagePrivateMembers() {
doTestWithDependency()
}
fun testAccessingProtectedMembers() {
doTestWithDependency()
}
fun testAccessingProtectedMembersFromKotlin() {
doTestWithDependency()
}
fun testOverridePackagePrivateMethod() {
doTestWithDependency()
}
override fun getBasePath() = "${KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH}/codeInspection/suspiciousPackagePrivateAccess"
}

View File

@@ -0,0 +1,72 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.SystemGetPropertyInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinSystemGetPropertyInspectionTest : SystemGetPropertyInspectionTestBase(), KotlinPluginModeProvider {
fun `test highlighting`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
fun foo() {
System.<warning descr="Call 'getProperty' can be simplified for 'file.separator'">getProperty</warning>("file.separator")
System.<warning descr="Call 'getProperty' can be simplified for 'path.separator'">getProperty</warning>("path.separator")
System.<warning descr="Call 'getProperty' can be simplified for 'line.separator'">getProperty</warning>("line.separator")
System.<warning descr="Call 'getProperty' can be simplified for 'file.encoding'">getProperty</warning>("file.encoding")
}
""".trimIndent())
}
fun `test quickfix file-separator`() {
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
fun foo() {
System.getPrope<caret>rty("file.separator")
}
""".trimIndent(), """
import java.nio.file.FileSystems
fun foo() {
FileSystems.getDefault().getSeparator()
}
""".trimIndent(), "Replace with 'java.nio.file.FileSystems.getDefault().getSeparator()'", true)
}
fun `test quickfix path-separator`() {
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
fun foo() {
System.getPrope<caret>rty("path.separator")
}
""".trimIndent(), """
import java.io.File
fun foo() {
File.pathSeparator
}
""".trimIndent(), "Replace with 'java.io.File.pathSeparator'", true)
}
fun `test quickfix line-separator`() {
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
fun foo() {
System.getPrope<caret>rty("line.separator")
}
""".trimIndent(), """
fun foo() {
System.lineSeparator()
}
""".trimIndent(), "Replace with 'java.lang.System.lineSeparator()'", true)
}
fun `test quickfix file-encoding`() {
myFixture.testQuickFix(JvmLanguage.KOTLIN, """
fun foo() {
System.getPrope<caret>rty("file.encoding")
}
""".trimIndent(), """
import java.nio.charset.Charset
fun foo() {
Charset.defaultCharset().displayName()
}
""".trimIndent(), "Replace with 'java.nio.charset.Charset.defaultCharset().displayName()'", true)
}
}

View File

@@ -0,0 +1,25 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.jvm.analysis.internal.testFramework.ThreadRunInspectionTestBase
import com.intellij.testFramework.TestDataPath
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
private const val inspectionPath = "/codeInspection/threadrun"
@TestDataPath("\$CONTENT_ROOT/testData$inspectionPath")
abstract class KotlinThreadRunInspectionTest : ThreadRunInspectionTestBase(), KotlinPluginModeProvider {
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + inspectionPath
fun `test highlighting`() {
myFixture.testHighlighting("ThreadRunTest.kt")
}
fun `test no highlighting super`() {
myFixture.testHighlighting("ThreadRunSuperTest.kt")
}
fun `test quickfix`() {
myFixture.testQuickFix("ThreadRunQfTest.kt")
}
}

View File

@@ -0,0 +1,321 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.tests.kotlin
import com.intellij.jvm.analysis.internal.testFramework.UnstableApiUsageInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinUnstableApiUsageInspectionTest : UnstableApiUsageInspectionTestBase(), KotlinPluginModeProvider {
fun `test kotlin unstable api usages`() {
inspection.myIgnoreInsideImports = false
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
@file:Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "UNUSED_VALUE", "UNUSED_PARAMETER", "UNUSED_VARIABLE")
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>
import experimental.pkg.<warning descr="'experimental.pkg.ClassWithExperimentalTypeInSignature' is unstable because its signature references unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">ClassWithExperimentalTypeInSignature</warning>
import experimental.pkg.OwnerOfMembersWithExperimentalTypesInSignature
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>.<warning descr="'NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</warning>
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>.<warning descr="'staticNonAnnotatedMethodInAnnotatedClass()' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">staticNonAnnotatedMethodInAnnotatedClass</warning>
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>.<warning descr="'ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is marked unstable with @ApiStatus.Experimental">ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</warning>
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>.<warning descr="'staticAnnotatedMethodInAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">staticAnnotatedMethodInAnnotatedClass</warning>
import experimental.pkg.NonAnnotatedClass
import experimental.pkg.NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS
import experimental.pkg.NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass
import experimental.pkg.NonAnnotatedClass.<warning descr="'ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS' is marked unstable with @ApiStatus.Experimental">ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS</warning>
import experimental.pkg.NonAnnotatedClass.<warning descr="'staticAnnotatedMethodInNonAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">staticAnnotatedMethodInNonAnnotatedClass</warning>
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedEnum' is marked unstable with @ApiStatus.Experimental">AnnotatedEnum</warning>
import experimental.pkg.NonAnnotatedEnum
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedEnum' is marked unstable with @ApiStatus.Experimental">AnnotatedEnum</warning>.<warning descr="'NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is declared in unstable enum 'experimental.pkg.AnnotatedEnum' marked with @ApiStatus.Experimental">NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM</warning>
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedEnum' is marked unstable with @ApiStatus.Experimental">AnnotatedEnum</warning>.<warning descr="'ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is marked unstable with @ApiStatus.Experimental">ANNOTATED_VALUE_IN_ANNOTATED_ENUM</warning>
import experimental.pkg.NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM
import experimental.pkg.NonAnnotatedEnum.<warning descr="'ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM' is marked unstable with @ApiStatus.Experimental">ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM</warning>
import experimental.pkg.<warning descr="'experimental.pkg.AnnotatedAnnotation' is marked unstable with @ApiStatus.Experimental">AnnotatedAnnotation</warning>
import experimental.pkg.NonAnnotatedAnnotation
import experimental.<warning descr="'experimental.annotatedPkg' is marked unstable with @ApiStatus.Experimental">annotatedPkg</warning>.<warning descr="'experimental.annotatedPkg.ClassInAnnotatedPkg' is declared in unstable package 'experimental.annotatedPkg' marked with @ApiStatus.Experimental">ClassInAnnotatedPkg</warning>
class UnstableElementsTest {
fun test() {
var s = <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>.<warning descr="'NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</warning>
<warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>.<warning descr="'staticNonAnnotatedMethodInAnnotatedClass()' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">staticNonAnnotatedMethodInAnnotatedClass</warning>()
val annotatedClassInstanceViaNonAnnotatedConstructor : <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning> = <warning descr="'AnnotatedClass()' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental"><warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning></warning>()
s = annotatedClassInstanceViaNonAnnotatedConstructor.<warning descr="'nonAnnotatedFieldInAnnotatedClass' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">nonAnnotatedFieldInAnnotatedClass</warning>
annotatedClassInstanceViaNonAnnotatedConstructor.<warning descr="'nonAnnotatedMethodInAnnotatedClass()' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">nonAnnotatedMethodInAnnotatedClass</warning>()
s = <warning descr="'NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</warning>
<warning descr="'staticNonAnnotatedMethodInAnnotatedClass()' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">staticNonAnnotatedMethodInAnnotatedClass</warning>()
s = <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>.<warning descr="'ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is marked unstable with @ApiStatus.Experimental">ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</warning>
<warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>.<warning descr="'staticAnnotatedMethodInAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">staticAnnotatedMethodInAnnotatedClass</warning>()
val annotatedClassInstanceViaAnnotatedConstructor : <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning> = <warning descr="'AnnotatedClass(java.lang.String)' is marked unstable with @ApiStatus.Experimental"><warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning></warning>("")
s = annotatedClassInstanceViaAnnotatedConstructor.<warning descr="'annotatedFieldInAnnotatedClass' is marked unstable with @ApiStatus.Experimental">annotatedFieldInAnnotatedClass</warning>
annotatedClassInstanceViaAnnotatedConstructor.<warning descr="'annotatedMethodInAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">annotatedMethodInAnnotatedClass</warning>()
s = <warning descr="'ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is marked unstable with @ApiStatus.Experimental">ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</warning>
<warning descr="'staticAnnotatedMethodInAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">staticAnnotatedMethodInAnnotatedClass</warning>()
// ---------------------------------
s = NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS
NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass()
val nonAnnotatedClassInstanceViaNonAnnotatedConstructor = NonAnnotatedClass()
s = nonAnnotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInNonAnnotatedClass
nonAnnotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInNonAnnotatedClass()
s = NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS
staticNonAnnotatedMethodInNonAnnotatedClass()
s = NonAnnotatedClass.<warning descr="'ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS' is marked unstable with @ApiStatus.Experimental">ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS</warning>
NonAnnotatedClass.<warning descr="'staticAnnotatedMethodInNonAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">staticAnnotatedMethodInNonAnnotatedClass</warning>()
val nonAnnotatedClassInstanceViaAnnotatedConstructor = <warning descr="'NonAnnotatedClass(java.lang.String)' is marked unstable with @ApiStatus.Experimental">NonAnnotatedClass</warning>("")
s = nonAnnotatedClassInstanceViaAnnotatedConstructor.<warning descr="'annotatedFieldInNonAnnotatedClass' is marked unstable with @ApiStatus.Experimental">annotatedFieldInNonAnnotatedClass</warning>
nonAnnotatedClassInstanceViaAnnotatedConstructor.<warning descr="'annotatedMethodInNonAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">annotatedMethodInNonAnnotatedClass</warning>()
s = <warning descr="'ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS' is marked unstable with @ApiStatus.Experimental">ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS</warning>
<warning descr="'staticAnnotatedMethodInNonAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">staticAnnotatedMethodInNonAnnotatedClass</warning>()
// ---------------------------------
var nonAnnotatedValueInAnnotatedEnum : <warning descr="'experimental.pkg.AnnotatedEnum' is marked unstable with @ApiStatus.Experimental">AnnotatedEnum</warning> = <warning descr="'experimental.pkg.AnnotatedEnum' is marked unstable with @ApiStatus.Experimental">AnnotatedEnum</warning>.<warning descr="'NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is declared in unstable enum 'experimental.pkg.AnnotatedEnum' marked with @ApiStatus.Experimental">NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM</warning>
nonAnnotatedValueInAnnotatedEnum = <warning descr="'NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is declared in unstable enum 'experimental.pkg.AnnotatedEnum' marked with @ApiStatus.Experimental">NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM</warning>
var annotatedValueInAnnotatedEnum : <warning descr="'experimental.pkg.AnnotatedEnum' is marked unstable with @ApiStatus.Experimental">AnnotatedEnum</warning> = <warning descr="'experimental.pkg.AnnotatedEnum' is marked unstable with @ApiStatus.Experimental">AnnotatedEnum</warning>.<warning descr="'ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is marked unstable with @ApiStatus.Experimental">ANNOTATED_VALUE_IN_ANNOTATED_ENUM</warning>
annotatedValueInAnnotatedEnum = <warning descr="'ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is marked unstable with @ApiStatus.Experimental">ANNOTATED_VALUE_IN_ANNOTATED_ENUM</warning>
var nonAnnotatedValueInNonAnnotatedEnum = NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM
nonAnnotatedValueInNonAnnotatedEnum = NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM
var annotatedValueInNonAnnotatedEnum = NonAnnotatedEnum.<warning descr="'ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM' is marked unstable with @ApiStatus.Experimental">ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM</warning>
annotatedValueInNonAnnotatedEnum = <warning descr="'ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM' is marked unstable with @ApiStatus.Experimental">ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM</warning>
// ---------------------------------
@<warning descr="'experimental.pkg.AnnotatedAnnotation' is marked unstable with @ApiStatus.Experimental">AnnotatedAnnotation</warning> class C1
@<warning descr="'experimental.pkg.AnnotatedAnnotation' is marked unstable with @ApiStatus.Experimental">AnnotatedAnnotation</warning>(<warning descr="'nonAnnotatedAttributeInAnnotatedAnnotation' is declared in unstable annotation 'experimental.pkg.AnnotatedAnnotation' marked with @ApiStatus.Experimental">nonAnnotatedAttributeInAnnotatedAnnotation</warning> = "123") class C2
@<warning descr="'experimental.pkg.AnnotatedAnnotation' is marked unstable with @ApiStatus.Experimental">AnnotatedAnnotation</warning>(<warning descr="'annotatedAttributeInAnnotatedAnnotation' is marked unstable with @ApiStatus.Experimental">annotatedAttributeInAnnotatedAnnotation</warning> = "123") class C3
@NonAnnotatedAnnotation class C4
@NonAnnotatedAnnotation(nonAnnotatedAttributeInNonAnnotatedAnnotation = "123") class C5
@NonAnnotatedAnnotation(<warning descr="'annotatedAttributeInNonAnnotatedAnnotation' is marked unstable with @ApiStatus.Experimental">annotatedAttributeInNonAnnotatedAnnotation</warning> = "123") class C6
}
}
open class DirectOverrideAnnotatedMethod : NonAnnotatedClass() {
override fun <warning descr="Overridden method 'annotatedMethodInNonAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">annotatedMethodInNonAnnotatedClass</warning>() {}
}
//No warning should be produced.
class IndirectOverrideAnnotatedMethod : DirectOverrideAnnotatedMethod() {
override fun annotatedMethodInNonAnnotatedClass() {}
}
class DirectOverrideNonAnnotatedMethodInAnnotatedClass : <warning descr="'AnnotatedClass()' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental"><warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning></warning>() {
override fun <warning descr="Overridden method 'nonAnnotatedMethodInAnnotatedClass()' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">nonAnnotatedMethodInAnnotatedClass</warning>() {}
}
class DirectOverrideAnnotatedMethodInAnnotatedClass : <warning descr="'AnnotatedClass()' is declared in unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental"><warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning></warning>() {
override fun <warning descr="Overridden method 'annotatedMethodInAnnotatedClass()' is marked unstable with @ApiStatus.Experimental">annotatedMethodInAnnotatedClass</warning>() {}
}
class WarningsOfExperimentalTypesInSignature {
fun classUsage() {
experimental.pkg.<warning descr="'experimental.pkg.ClassWithExperimentalTypeInSignature' is unstable because its signature references unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">ClassWithExperimentalTypeInSignature</warning><<warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>>()
}
fun membersUsages(owner: OwnerOfMembersWithExperimentalTypesInSignature) {
val field = owner.<warning descr="'field' is unstable because its signature references unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">field</warning>
owner.<warning descr="'parameterType(experimental.pkg.AnnotatedClass)' is unstable because its signature references unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">parameterType</warning>(null)
owner.<warning descr="'returnType()' is unstable because its signature references unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">returnType</warning>()
val fieldPkg = owner.<warning descr="'field' is unstable because its signature references unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">field</warning>
owner.<warning descr="'parameterTypePkg(experimental.annotatedPkg.ClassInAnnotatedPkg)' is unstable because its signature references unstable class 'experimental.annotatedPkg.ClassInAnnotatedPkg' marked with @ApiStatus.Experimental">parameterTypePkg</warning>(null)
owner.<warning descr="'returnTypePkg()' is unstable because its signature references unstable class 'experimental.pkg.AnnotatedClass' marked with @ApiStatus.Experimental">returnTypePkg</warning>()
}
}
""".trimIndent())
}
fun `test kotlin no warnings on access to members of the same file`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
@file:Suppress("UNUSED_PARAMETER")
package test;
import experimental.pkg.AnnotatedClass;
class NoWarningsMembersOfTheSameFile {
companion object {
private var staticField: <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>? = null;
private fun staticReturnType(): <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>? { return null; }
private fun staticParamType(param: <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>?) { }
}
private var field: <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>? = null;
private fun returnType(): <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>? { return null; }
private fun paramType(param: <warning descr="'experimental.pkg.AnnotatedClass' is marked unstable with @ApiStatus.Experimental">AnnotatedClass</warning>?) { }
fun testNoWarningsProducedForMembersOfTheSameClass() {
field?.toString();
staticField?.toString();
returnType();
paramType(null);
staticReturnType();
staticParamType(null)
}
private inner class InnerClass {
fun testNoWarningsProducedForMembersEnclosingClass() {
field.toString();
staticField.toString();
returnType();
paramType(null);
staticReturnType();
staticParamType(null)
}
}
}
""".trimIndent())
}
fun `test kotlin do not report unstable api usages inside import statements`() {
inspection.myIgnoreInsideImports = true
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import experimental.pkg.AnnotatedClass
import experimental.pkg.AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
import experimental.pkg.AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass
import experimental.pkg.AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
import experimental.pkg.AnnotatedClass.staticAnnotatedMethodInAnnotatedClass
import experimental.pkg.NonAnnotatedClass
import experimental.pkg.NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS
import experimental.pkg.NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass
import experimental.pkg.NonAnnotatedClass.ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS
import experimental.pkg.NonAnnotatedClass.staticAnnotatedMethodInNonAnnotatedClass
import experimental.pkg.AnnotatedEnum
import experimental.pkg.NonAnnotatedEnum
import experimental.pkg.AnnotatedEnum.NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM
import experimental.pkg.AnnotatedEnum.ANNOTATED_VALUE_IN_ANNOTATED_ENUM
import experimental.pkg.NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM
import experimental.pkg.NonAnnotatedEnum.ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM
import experimental.pkg.AnnotatedAnnotation
import experimental.pkg.NonAnnotatedAnnotation
import experimental.annotatedPkg.ClassInAnnotatedPkg
""".trimIndent())
}
fun `test Kotlin scheduled for removal`() {
inspection.myIgnoreInsideImports = false
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
@file:Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "UNUSED_VALUE", "UNUSED_PARAMETER", "UNUSED_VARIABLE")
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.ClassWithScheduledForRemovalTypeInSignature' is scheduled for removal because its signature references class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">ClassWithScheduledForRemovalTypeInSignature</error>
import scheduledForRemoval.pkg.OwnerOfMembersWithScheduledForRemovalTypesInSignature
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>.<error descr="'NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</error>
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>.<error descr="'staticNonAnnotatedMethodInAnnotatedClass()' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">staticNonAnnotatedMethodInAnnotatedClass</error>
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>.<error descr="'ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is scheduled for removal in version 123.456">ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</error>
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>.<error descr="'staticAnnotatedMethodInAnnotatedClass()' is scheduled for removal in version 123.456">staticAnnotatedMethodInAnnotatedClass</error>
import scheduledForRemoval.pkg.NonAnnotatedClass
import scheduledForRemoval.pkg.NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS
import scheduledForRemoval.pkg.NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass
import scheduledForRemoval.pkg.NonAnnotatedClass.<error descr="'ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS' is scheduled for removal in version 123.456">ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS</error>
import scheduledForRemoval.pkg.NonAnnotatedClass.<error descr="'staticAnnotatedMethodInNonAnnotatedClass()' is scheduled for removal in version 123.456">staticAnnotatedMethodInNonAnnotatedClass</error>
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedEnum' is scheduled for removal in version 123.456">AnnotatedEnum</error>
import scheduledForRemoval.pkg.NonAnnotatedEnum
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedEnum' is scheduled for removal in version 123.456">AnnotatedEnum</error>.<error descr="'NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is declared in enum 'scheduledForRemoval.pkg.AnnotatedEnum' scheduled for removal in version 123.456">NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM</error>
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedEnum' is scheduled for removal in version 123.456">AnnotatedEnum</error>.<error descr="'ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is scheduled for removal in version 123.456">ANNOTATED_VALUE_IN_ANNOTATED_ENUM</error>
import scheduledForRemoval.pkg.NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM
import scheduledForRemoval.pkg.NonAnnotatedEnum.<error descr="'ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM' is scheduled for removal in version 123.456">ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM</error>
import scheduledForRemoval.pkg.<error descr="'scheduledForRemoval.pkg.AnnotatedAnnotation' is scheduled for removal in version 123.456">AnnotatedAnnotation</error>
import scheduledForRemoval.pkg.NonAnnotatedAnnotation
import scheduledForRemoval.<error descr="'scheduledForRemoval.annotatedPkg' is scheduled for removal in version 123.456">annotatedPkg</error>.<error descr="'scheduledForRemoval.annotatedPkg.ClassInAnnotatedPkg' is declared in package 'scheduledForRemoval.annotatedPkg' scheduled for removal in version 123.456">ClassInAnnotatedPkg</error>
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "UNUSED_VALUE")
class ScheduledForRemovalElementsTest {
fun test() {
var s = <error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>.<error descr="'NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</error>
<error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>.<error descr="'staticNonAnnotatedMethodInAnnotatedClass()' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">staticNonAnnotatedMethodInAnnotatedClass</error>()
val annotatedClassInstanceViaNonAnnotatedConstructor : <error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error> = <error descr="'AnnotatedClass()' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456"><error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error></error>()
s = annotatedClassInstanceViaNonAnnotatedConstructor.<error descr="'nonAnnotatedFieldInAnnotatedClass' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">nonAnnotatedFieldInAnnotatedClass</error>
annotatedClassInstanceViaNonAnnotatedConstructor.<error descr="'nonAnnotatedMethodInAnnotatedClass()' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">nonAnnotatedMethodInAnnotatedClass</error>()
s = <error descr="'NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</error>
<error descr="'staticNonAnnotatedMethodInAnnotatedClass()' is declared in class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">staticNonAnnotatedMethodInAnnotatedClass</error>()
s = <error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>.<error descr="'ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is scheduled for removal in version 123.456">ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</error>
<error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>.<error descr="'staticAnnotatedMethodInAnnotatedClass()' is scheduled for removal in version 123.456">staticAnnotatedMethodInAnnotatedClass</error>()
val annotatedClassInstanceViaAnnotatedConstructor : <error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error> = <error descr="'AnnotatedClass(java.lang.String)' is scheduled for removal in version 123.456"><error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error></error>("")
s = annotatedClassInstanceViaAnnotatedConstructor.<error descr="'annotatedFieldInAnnotatedClass' is scheduled for removal in version 123.456">annotatedFieldInAnnotatedClass</error>
annotatedClassInstanceViaAnnotatedConstructor.<error descr="'annotatedMethodInAnnotatedClass()' is scheduled for removal in version 123.456">annotatedMethodInAnnotatedClass</error>()
s = <error descr="'ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS' is scheduled for removal in version 123.456">ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS</error>
<error descr="'staticAnnotatedMethodInAnnotatedClass()' is scheduled for removal in version 123.456">staticAnnotatedMethodInAnnotatedClass</error>()
// ---------------------------------
s = NonAnnotatedClass.NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS
NonAnnotatedClass.staticNonAnnotatedMethodInNonAnnotatedClass()
val nonAnnotatedClassInstanceViaNonAnnotatedConstructor = NonAnnotatedClass()
s = nonAnnotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInNonAnnotatedClass
nonAnnotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInNonAnnotatedClass()
s = NON_ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS
staticNonAnnotatedMethodInNonAnnotatedClass()
s = NonAnnotatedClass.<error descr="'ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS' is scheduled for removal in version 123.456">ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS</error>
NonAnnotatedClass.<error descr="'staticAnnotatedMethodInNonAnnotatedClass()' is scheduled for removal in version 123.456">staticAnnotatedMethodInNonAnnotatedClass</error>()
val nonAnnotatedClassInstanceViaAnnotatedConstructor = <error descr="'NonAnnotatedClass(java.lang.String)' is scheduled for removal in version 123.456">NonAnnotatedClass</error>("")
s = nonAnnotatedClassInstanceViaAnnotatedConstructor.<error descr="'annotatedFieldInNonAnnotatedClass' is scheduled for removal in version 123.456">annotatedFieldInNonAnnotatedClass</error>
nonAnnotatedClassInstanceViaAnnotatedConstructor.<error descr="'annotatedMethodInNonAnnotatedClass()' is scheduled for removal in version 123.456">annotatedMethodInNonAnnotatedClass</error>()
s = <error descr="'ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS' is scheduled for removal in version 123.456">ANNOTATED_CONSTANT_IN_NON_ANNOTATED_CLASS</error>
<error descr="'staticAnnotatedMethodInNonAnnotatedClass()' is scheduled for removal in version 123.456">staticAnnotatedMethodInNonAnnotatedClass</error>()
// ---------------------------------
var nonAnnotatedValueInAnnotatedEnum : <error descr="'scheduledForRemoval.pkg.AnnotatedEnum' is scheduled for removal in version 123.456">AnnotatedEnum</error> = <error descr="'scheduledForRemoval.pkg.AnnotatedEnum' is scheduled for removal in version 123.456">AnnotatedEnum</error>.<error descr="'NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is declared in enum 'scheduledForRemoval.pkg.AnnotatedEnum' scheduled for removal in version 123.456">NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM</error>
nonAnnotatedValueInAnnotatedEnum = <error descr="'NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is declared in enum 'scheduledForRemoval.pkg.AnnotatedEnum' scheduled for removal in version 123.456">NON_ANNOTATED_VALUE_IN_ANNOTATED_ENUM</error>
var annotatedValueInAnnotatedEnum : <error descr="'scheduledForRemoval.pkg.AnnotatedEnum' is scheduled for removal in version 123.456">AnnotatedEnum</error> = <error descr="'scheduledForRemoval.pkg.AnnotatedEnum' is scheduled for removal in version 123.456">AnnotatedEnum</error>.<error descr="'ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is scheduled for removal in version 123.456">ANNOTATED_VALUE_IN_ANNOTATED_ENUM</error>
annotatedValueInAnnotatedEnum = <error descr="'ANNOTATED_VALUE_IN_ANNOTATED_ENUM' is scheduled for removal in version 123.456">ANNOTATED_VALUE_IN_ANNOTATED_ENUM</error>
var nonAnnotatedValueInNonAnnotatedEnum = NonAnnotatedEnum.NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM
nonAnnotatedValueInNonAnnotatedEnum = NON_ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM
var annotatedValueInNonAnnotatedEnum = NonAnnotatedEnum.<error descr="'ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM' is scheduled for removal in version 123.456">ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM</error>
annotatedValueInNonAnnotatedEnum = <error descr="'ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM' is scheduled for removal in version 123.456">ANNOTATED_VALUE_IN_NON_ANNOTATED_ENUM</error>
// ---------------------------------
@<error descr="'scheduledForRemoval.pkg.AnnotatedAnnotation' is scheduled for removal in version 123.456">AnnotatedAnnotation</error> class C1
@<error descr="'scheduledForRemoval.pkg.AnnotatedAnnotation' is scheduled for removal in version 123.456">AnnotatedAnnotation</error>(<error descr="'nonAnnotatedAttributeInAnnotatedAnnotation' is declared in annotation 'scheduledForRemoval.pkg.AnnotatedAnnotation' scheduled for removal in version 123.456">nonAnnotatedAttributeInAnnotatedAnnotation</error> = "123") class C2
@<error descr="'scheduledForRemoval.pkg.AnnotatedAnnotation' is scheduled for removal in version 123.456">AnnotatedAnnotation</error>(<error descr="'annotatedAttributeInAnnotatedAnnotation' is scheduled for removal in version 123.456">annotatedAttributeInAnnotatedAnnotation</error> = "123") class C3
@NonAnnotatedAnnotation class C4
@NonAnnotatedAnnotation(nonAnnotatedAttributeInNonAnnotatedAnnotation = "123") class C5
@NonAnnotatedAnnotation(<error descr="'annotatedAttributeInNonAnnotatedAnnotation' is scheduled for removal in version 123.456">annotatedAttributeInNonAnnotatedAnnotation</error> = "123") class C6
}
}
open class DirectOverrideAnnotatedMethod : NonAnnotatedClass() {
override fun <error descr="Overridden method 'annotatedMethodInNonAnnotatedClass()' is scheduled for removal in version 123.456">annotatedMethodInNonAnnotatedClass</error>() {}
}
//No warning should be produced.
class IndirectOverrideAnnotatedMethod : DirectOverrideAnnotatedMethod() {
override fun annotatedMethodInNonAnnotatedClass() {}
}
class WarningsOfScheduledForRemovalTypesInSignature {
fun classUsage() {
<error descr="'scheduledForRemoval.pkg.ClassWithScheduledForRemovalTypeInSignature' is scheduled for removal because its signature references class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">ClassWithScheduledForRemovalTypeInSignature</error><<error descr="'scheduledForRemoval.pkg.AnnotatedClass' is scheduled for removal in version 123.456">AnnotatedClass</error>>()
}
fun membersUsages(owner: OwnerOfMembersWithScheduledForRemovalTypesInSignature) {
val field = owner.<error descr="'field' is scheduled for removal because its signature references class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">field</error>
owner.<error descr="'parameterType(scheduledForRemoval.pkg.AnnotatedClass)' is scheduled for removal because its signature references class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">parameterType</error>(null)
owner.<error descr="'returnType()' is scheduled for removal because its signature references class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">returnType</error>()
val fieldPkg = owner.<error descr="'field' is scheduled for removal because its signature references class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">field</error>
owner.<error descr="'parameterTypePkg(scheduledForRemoval.annotatedPkg.ClassInAnnotatedPkg)' is scheduled for removal because its signature references class 'scheduledForRemoval.annotatedPkg.ClassInAnnotatedPkg' scheduled for removal in version 123.456">parameterTypePkg</error>(null)
owner.<error descr="'returnTypePkg()' is scheduled for removal because its signature references class 'scheduledForRemoval.pkg.AnnotatedClass' scheduled for removal in version 123.456">returnTypePkg</error>()
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,69 @@
package com.intellij.codeInspection.tests.kotlin
import com.intellij.codeInspection.UnstableTypeUsedInSignatureInspection
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.openapi.application.PathManager
import com.intellij.testFramework.TestDataPath
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
import com.intellij.util.PathUtil
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
import java.io.File
@TestDataPath("\$CONTENT_ROOT/testData/codeInspection/unstableTypeUsedInSignature")
abstract class KotlinUnstableTypeUsedInSignatureTest : JavaCodeInsightFixtureTestCase(), KotlinPluginModeProvider {
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + "/codeInspection/unstableTypeUsedInSignature"
override fun getTestDataPath(): String = PathManager.getCommunityHomePath().replace(File.separatorChar, '/') + basePath
override fun setUp() {
super.setUp()
val inspection = UnstableTypeUsedInSignatureInspection()
inspection.unstableApiAnnotations.clear()
inspection.unstableApiAnnotations.add(ApiStatus.Experimental::class.java.canonicalName)
myFixture.enableInspections(inspection)
configureAnnotatedFiles()
}
private fun configureAnnotatedFiles() {
listOf(
"experimentalPackage/ClassInExperimentalPackage.java",
"experimentalPackage/package-info.java",
"experimentalPackage/NoWarnings.java",
"test/ExperimentalClass.java"
).forEach { myFixture.copyFileToProject(it) }
}
override fun tuneFixture(moduleBuilder: JavaModuleFixtureBuilder<*>) {
moduleBuilder.addLibrary("util", PathUtil.getJarPathForClass(ApiStatus.Experimental::class.java))
}
fun `test kotlin missing unstable annotation`() {
myFixture.testHighlighting("UnstableTypeUsedInSignature.kt")
myFixture.testHighlighting("UnstableTypeUsedInTypeParameter.kt")
}
fun `test java missing unstable annotation`() {
myFixture.testHighlighting("UnstableTypeUsedInSignature.java")
myFixture.testHighlighting("UnstableTypeUsedInTypeParameter.java")
}
fun `test java no extra warnings are produced`() {
myFixture.testHighlighting("noWarnings/NoWarningsAlreadyMarked.java")
myFixture.testHighlighting("noWarnings/NoWarningsInaccessible.java")
myFixture.testHighlighting("noWarnings/NoWarningsMembersAlreadyMarked.java")
}
fun `test kotlin no extra warnings are produced`() {
myFixture.testHighlighting("noWarnings/NoWarningsAlreadyMarked.kt")
myFixture.testHighlighting("noWarnings/NoWarningsInaccessible.kt")
}
fun `test no warnings produced in experimental package`() {
myFixture.testHighlighting("experimentalPackage/NoWarnings.java")
}
}

View File

@@ -0,0 +1,150 @@
package com.intellij.codeInspection.tests.kotlin;
import com.intellij.java.codeInspection.AbstractUnusedDeclarationTest;
import com.intellij.java.codeInspection.UnusedDeclarationInspectionTest;
import com.intellij.openapi.application.ex.PathManagerEx;
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider;
public abstract class KotlinUnusedDeclarationTest extends AbstractUnusedDeclarationTest implements KotlinPluginModeProvider {
@Override
protected String getTestDataPath() {
return PathManagerEx.getTestDataPath(UnusedDeclarationInspectionTest.class) + "/inspection/jvm";
}
public void testSingleton() {
myTool.ADD_NONJAVA_TO_ENTRIES = false;
doTest();
}
//TODO
public void testDefaultConstructor() {
doTest();
}
public void testReferenceFromSuperTypeList() {
doTest();
}
public void testImplementedInterface() {
doTest();
}
public void testReachableFromMain() {
myTool.ADD_MAINS_TO_ENTRIES = true;
doTest();
}
public void testMutableCalls() {
doTest();
}
public void testOverridingProperty() {
myTool.getSharedLocalInspectionTool().PARAMETER = true;
myTool.getSharedLocalInspectionTool().LOCAL_VARIABLE = false;
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testPrimaryConstructor() {
doTest();
}
public void testPrimaryConstructor2() {
myTool.getSharedLocalInspectionTool().PARAMETER = true;
myTool.getSharedLocalInspectionTool().LOCAL_VARIABLE = false;
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testReceiverParameter() {
myTool.getSharedLocalInspectionTool().PARAMETER = true;
myTool.getSharedLocalInspectionTool().LOCAL_VARIABLE = false;
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testOperatorInWhen() {
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testCallableParameter() {
myTool.getSharedLocalInspectionTool().PARAMETER = true;
myTool.getSharedLocalInspectionTool().LOCAL_VARIABLE = false;
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testCallableProperty() {
myTool.getSharedLocalInspectionTool().FIELD = true;
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testUsagesInCallableReceiver() {
myTool.getSharedLocalInspectionTool().PARAMETER = true;
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testUsagesInClassLiteral() {
myTool.getSharedLocalInspectionTool().PARAMETER = true;
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testMainParameter() {
myTool.getSharedLocalInspectionTool().PARAMETER = true;
myTool.getSharedLocalInspectionTool().LOCAL_VARIABLE = false;
doTest("deadCode/" + getTestName(true), myToolWrapper);
}
public void testStaticMethods() {
doTest();
}
public void testChainOfCalls() {
doTest();
}
public void testReachableFromFieldInitializer() {
doTest();
}
public void testReachableFromFieldArrayInitializer() {
doTest();
}
public void testJunitEntryPoint() {
doTest();
}
public void testConstructorCalls() {
doTest();
}
public void testPropertyReference() {
doTest();
}
public void testReferenceInLambda() {
doTest();
}
public void testNonJvmReferences() {
doTest();
}
//TODO uast kotlin enum support
public void _testEnumInstantiation() {
doTest();
}
public void _testEnumValues() {
doTest();
}
public void testUsagesInAnonymous() {
doTest();
}
public void testClassLiteralRef() {
doTest();
}
public void testTopLevelFunction() {
doTest();
}
}

View File

@@ -0,0 +1,24 @@
package com.intellij.codeInspection.tests.kotlin;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.unusedReturnValue.UnusedReturnValue;
import com.intellij.testFramework.JavaInspectionTestCase;
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider;
public abstract class KotlinUnusedReturnValueInspectionKtTest extends JavaInspectionTestCase implements KotlinPluginModeProvider {
private final UnusedReturnValue myGlobalTool = new UnusedReturnValue();
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/inspection/jvm";
}
private String getGlobalTestDir() {
return "unusedReturnValue/" + getTestName(true);
}
public void testUsedPropertyBySimpleName() {
doTest(getGlobalTestDir(), myGlobalTool);
}
}

View File

@@ -0,0 +1,22 @@
package com.intellij.codeInspection.tests.kotlin.deadCode
import com.intellij.jvm.analysis.internal.testFramework.deadCode.AssertJImplicitUsageProviderTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinAssertJImplicitUsageProviderTest : AssertJImplicitUsageProviderTestBase(), KotlinPluginModeProvider {
fun `test inject soft assertion implicit usage provider`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
@org.junit.jupiter.api.extension.ExtendWith(org.assertj.core.api.junit.jupiter.SoftAssertionsExtension::class)
class TestClass {
@org.assertj.core.api.junit.jupiter.InjectSoftAssertions
private lateinit var softAssertions: org.assertj.core.api.SoftAssertions
@org.junit.jupiter.api.Test
fun doSomething() {
softAssertions.assertThat("string").isEqualTo("string")
}
}
""".trimIndent(), fileName = "TestClass")
}
}

View File

@@ -0,0 +1,23 @@
package com.intellij.codeInspection.tests.kotlin.deadCode
import com.intellij.jvm.analysis.internal.testFramework.deadCode.EasyMockImplicitUsageProviderTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinEasyMockImplicitUsageProviderTest : EasyMockImplicitUsageProviderTestBase(), KotlinPluginModeProvider {
fun `test implicit usage for mocked field`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class MyEasyMockTest {
@org.easymock.Mock
private lateinit var myFoo: String
init {
System.out.println(myFoo)
}
@org.junit.Test
fun testName() { }
}
""".trimIndent(), fileName = "MyEasyMockTest")
}
}

View File

@@ -0,0 +1,23 @@
package com.intellij.codeInspection.tests.kotlin.deadCode
import com.intellij.jvm.analysis.internal.testFramework.deadCode.MockitoImplicitUsageProviderTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinMockitoImplicitUsageProviderTest : MockitoImplicitUsageProviderTestBase(), KotlinPluginModeProvider {
fun `test implicit usage for mocked field`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class MyMockitoTest {
@org.mockito.Mock
private lateinit var myFoo: String
init {
System.out.println(myFoo)
}
@org.junit.Test
fun testName() { }
}
""".trimIndent(), fileName = "MyMockitoTest")
}
}

View File

@@ -0,0 +1,198 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingConditionDisagreesWithLogLevelStatementInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinLoggingConditionDisagreesWithLogLevelStatementInspectionTest : LoggingConditionDisagreesWithLogLevelStatementInspectionTestBase(), KotlinPluginModeProvider {
fun `test slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
fun n() {
if (LOG.isInfoEnabled) {
LOG.info("nothing to report")
}
if (LOG.<warning descr="Level of condition 'INFO' does not match level of logging call 'WARN'">isInfoEnabled</warning>) {
LOG.warn("test")
}
if (LOG.<warning descr="Level of condition 'INFO' does not match level of logging call 'WARN'">isInfoEnabled</warning>()) {
LOG.warn("test")
}
}
companion object {
private val LOG = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test log4j2`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
internal class Y {
fun m() {
if (LOG.isWarnEnabled()) {
LOG.warn("test")
}
if (LOG.isWarnEnabled ) {
LOG.warn("test")
}
if (LOG.<warning descr="Level of condition 'INFO' does not match level of logging call 'WARN'">isInfoEnabled</warning> ) {
LOG.warn("test")
}
if (LOG.<warning descr="Level of condition 'INFO' does not match level of logging call 'WARN'">isInfoEnabled</warning>()) {
LOG.warn("test")
}
}
companion object {
val LOG = LogManager.getLogger()
}
}
""".trimIndent())
}
fun `test several logs`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal object X {
private val logger = LoggerFactory.getLogger()
fun test1() {
if (logger.isDebugEnabled) {
try {
something()
logger.debug("a")
} catch (e: Exception) {
logger.error("a")
}
}
if (logger.isDebugEnabled()) {
try {
something()
logger.debug("a")
} catch (e: Exception) {
logger.error("a")
}
}
if (logger.<warning descr="Level of condition 'INFO' does not match level of logging call 'DEBUG'"><warning descr="Level of condition 'INFO' does not match level of logging call 'ERROR'">isInfoEnabled</warning></warning>) {
try {
something()
logger.debug("a")
} catch (e: Exception) {
logger.error("a")
}
}
if (logger.<warning descr="Level of condition 'INFO' does not match level of logging call 'DEBUG'"><warning descr="Level of condition 'INFO' does not match level of logging call 'ERROR'">isInfoEnabled</warning></warning>()) {
try {
something()
logger.debug("a")
} catch (e: Exception) {
logger.error("a")
}
}
}
private fun something() {}
}
""".trimIndent())
}
fun `test java util logging`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.util.logging.Level
import java.util.logging.Logger
val LOG = Logger.getLogger("")
internal class Loggers {
fun method() {
if (LOG.<warning descr="Level of condition 'FINE' does not match level of logging call 'WARNING'">isLoggable</warning>(Level.FINE)) {
LOG.warning("test")
}
if (LOG.isLoggable(Level.WARNING)) {
LOG.warning("not error")
}
}
}
""".trimIndent())
}
fun `test fixes slf4j change guards`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal object Slf4J {
private val log: Logger = LoggerFactory.getLogger(Slf4J::class.java)
private fun request1(i: String) {
val msg = "log messages2: {}"
if (log.isDeb<caret>ugEnabled) {
log.info(msg, i)
}
}
}""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal object Slf4J {
private val log: Logger = LoggerFactory.getLogger(Slf4J::class.java)
private fun request1(i: String) {
val msg = "log messages2: {}"
if (log.isInfoEnabled) {
log.info(msg, i)
}
}
}""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.logging.condition.disagrees.with.log.statement.fix.name", 0)
)
}
fun `test fixes slf4j change guards as methods`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal object Slf4J {
private val log: Logger = LoggerFactory.getLogger(Slf4J::class.java)
private fun request1(i: String) {
val msg = "log messages2: {}"
if (log.isDeb<caret>ugEnabled()) {
log.info(msg, i)
}
}
}""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal object Slf4J {
private val log: Logger = LoggerFactory.getLogger(Slf4J::class.java)
private fun request1(i: String) {
val msg = "log messages2: {}"
if (log.isInfoEnabled()) {
log.info(msg, i)
}
}
}""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.logging.condition.disagrees.with.log.statement.fix.name", 0)
)
}
}

View File

@@ -0,0 +1,289 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingGuardedByConditionInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinLoggingGuardedByConditionInspectionTest : LoggingGuardedByConditionInspectionTestBase(), KotlinPluginModeProvider {
fun `test slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.<warning descr="Logging call guarded by log condition">isDebugEnabled</warning>) {
LOG.debug("test" + arg)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test log4j2`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
internal class X {
fun n(arg: String) {
if (LOG.<warning descr="Logging call guarded by log condition">isDebugEnabled()</warning>) {
LOG.debug("test1" + arg)
}
}
companion object {
val LOG: Logger = LogManager.getLogger()
}
}
""".trimIndent())
}
fun `test several calls slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.<warning descr="Logging call guarded by log condition">isDebugEnabled</warning>) {
LOG.debug("test1" + arg)
LOG.debug("test2" + arg)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test several different calls slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.<warning descr="Logging call guarded by log condition">isDebugEnabled</warning>) {
LOG.info("test1" + arg)
LOG.debug("test2" + arg)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test several calls with another call slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.<warning descr="Logging call guarded by log condition">isDebugEnabled</warning>) {
LOG.debug("test1" + arg)
arg.toString()
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test several calls with another call slf4j showOnlyIfFixPossible`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.isDebugEnabled) {
LOG.debug("test1" + arg)
arg.toString()
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test slf4j fix`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.<caret>isDebugEnabled) {
LOG.debug("test" + arg)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
LOG.debug("test" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.log.guarded.fix.family.name")
)
}
fun `test slf4j without block fix`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.<caret>isDebugEnabled)
LOG.debug("test" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
LOG.debug("test" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.log.guarded.fix.family.name")
)
}
fun `test several calls slf4j fix`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.<caret>isDebugEnabled) {
LOG.debug("test1" + arg)
LOG.debug("test2" + arg)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
LOG.debug("test1" + arg)
LOG.debug("test2" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.log.guarded.fix.family.name")
)
}
fun `test slf4j with comment fix`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.<caret>isDebugEnabled) {//comment1
//comment2
LOG.debug("test" + arg)
//comment3
//comment4
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
//comment1
//comment2
LOG.debug("test" + arg)
//comment3
//comment4
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.log.guarded.fix.family.name")
)
}
}

View File

@@ -0,0 +1,225 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionLog4J2Test : LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase(), KotlinPluginModeProvider {
fun `test log4j2 with text variables`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
internal class Logging {
fun m(i: Int) {
val text = "test {}{}{}"
LOG.info( <warning descr="Fewer arguments provided (1) than placeholders specified (3)">text</warning> , i)
val text2 = "test "
LOG.fatal( <warning descr="More arguments provided (1) than placeholders specified (0)">text2</warning> , i)
LOG.fatal( <warning descr="Fewer arguments provided (1) than placeholders specified (6)">text + text</warning> , i)
LOG.fatal( <warning descr="Fewer arguments provided (1) than placeholders specified (18)">text + text + text + text + text + text</warning> , i)
LOG.info( <warning descr="More arguments provided (1) than placeholders specified (0)">FINAL_TEXT</warning> , i)
val sum = "first {}" + "second {}" + 1
LOG.info( <warning descr="Fewer arguments provided (1) than placeholders specified (2)">sum</warning> , i)
}
companion object {
private const val FINAL_TEXT = "const"
private val LOG = LogManager.getLogger()
}
}
""".trimIndent())
}
fun `test log4j2`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.*;
private val logger = LogManager.getLogger();
private val brackets: String = "{}"
fun foo() {
logger?.debug(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.debug(<warning descr="More arguments provided (2) than placeholders specified (1)">"test " + brackets</warning>, 1, 2) //warn
logger?.debug("test {}" + brackets, 1, 2)
logger?.debug(<warning descr="Fewer arguments provided (1) than placeholders specified (3)">"test {} {} {}"</warning>, 1, Exception()) //warn
logger?.debug(<warning descr="More arguments provided (1) than placeholders specified (0)">"test"</warning>, 1, Exception()) //warn
logger?.debug(<warning descr="Fewer arguments provided (0) than placeholders specified (1)">"test {}"</warning>, Exception()) //warn
logger?.debug("test {} {}", 1, 2, Exception())
logger?.debug("test {} {}", 1, Exception())
logger?.debug(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.debug("test {} {}", 1, 2, Exception())
logger?.debug(<warning descr="More arguments provided (2) than placeholders specified (1)">"test {}"</warning>, 1, 2) //warn
logger?.error(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.fatal(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.info(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.trace(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.warn(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.info("test {} {}", {}, {}, {RuntimeException()})
logger?.info("test {} {}", {}, {RuntimeException()})
}
""".trimIndent())
}
fun `test log4j2 builder`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.*;
private val logger = LogManager.getLogger();
private val brackets: String = "{}"
fun foo() {
logger?.atDebug()?.log(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.atDebug()?.log("test {} {}", 1, 2)
logger?.atDebug()?.log(<warning descr="More arguments provided (2) than placeholders specified (0)">"test"</warning>, 1, Exception()) //warn
logger?.atDebug()?.log(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, Exception()) //warn
logger?.atDebug()?.log("test {} {} {}", 1, 2, Exception())
logger?.atDebug()?.log(<warning descr="More arguments provided (3) than placeholders specified (2)">"test {} {}"</warning>, 1, 2, Exception()) //warn
logger?.atDebug()?.log("test {}", Exception())
logger?.atDebug()?.log(<warning descr="More arguments provided (1) than placeholders specified (0)">"test"</warning>, Exception()) //warn
}
internal class Logging {
fun m(i: Int) {
LOG.atInfo().log( <warning descr="Fewer arguments provided (1) than placeholders specified (3)">"test? {}{}{}"</warning> , i)
LOG.atFatal().log( <warning descr="More arguments provided (2) than placeholders specified (0)">"test "</warning> , i, i)
}
companion object {
private val LOG = LogManager.getLogger()
}
}
""".trimIndent())
}
fun `test log4j2LogBuilder 2`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.util.Supplier
internal class Logging {
fun m(i: Int) {
LOG.atInfo().log( <warning descr="Fewer arguments provided (1) than placeholders specified (3)">"test? {}{}{}"</warning> , i)
LOG.atFatal().log( <warning descr="More arguments provided (2) than placeholders specified (0)">"test "</warning> , i, i)
LOG.atError().log( <warning descr="More arguments provided (1) than placeholders specified (0)">"test? "</warning> , Supplier<Any> { "" })
}
companion object {
private val LOG = LogManager.getLogger()
}
}
""".trimIndent())
}
fun `test formatted log4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.*;
private val LOG: Logger = LogManager.getFormatterLogger()
fun m() {
try {
throw RuntimeException()
} catch (t: Throwable) {
val LOG2: Logger = LogManager.getFormatterLogger()
LOG.info("My %s text", "test", t)
LOG.info( <warning descr="Illegal format string specifier">"My %i text"</warning> , "test")
LOG.info( <warning descr="More arguments provided (2) than placeholders specified (1)">"My %s text"</warning> , "test1", "test2")
LOG2.info("My %s text, %s", "test1") //skip because LOG2 is not final
LogManager.getFormatterLogger().info( <warning descr="Fewer arguments provided (1) than placeholders specified (2)">"My %s text, %s"</warning> , "test1")
}
}
""".trimIndent())
}
fun `test Log4j2 with exception in suppliers`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.util.Supplier
internal class Logging {
fun m() {
try {
throw java.lang.RuntimeException()
} catch (t: IllegalArgumentException) {
LOG.info( <warning descr="More arguments provided (3) than placeholders specified (1)">"test {}"</warning> , Supplier<Any> { "test" }, Supplier<Any> { "test" }, Supplier<Any> { t })
} catch (t: IllegalStateException) {
LOG.info(<warning descr="More arguments provided (3) than placeholders specified (1)">"test {}"</warning>, Supplier<Any> { "test" }, Supplier<Any> { "test" }, Supplier<Any> { t })
} catch (t: Throwable) {
LOG.info("test {}", Supplier<Any> { "test" }, Supplier<Any> { t })
LOG.info( <warning descr="More arguments provided (3) than placeholders specified (1)">"test {}"</warning> , Supplier<Any> { "test" }, Supplier<Any> { "test" }, Supplier<Any> { t })
val s = Supplier { t }
LOG.info("test {}", Supplier<Any> { "test" }, s)
val s2: Supplier<*> = Supplier<Any> { t }
LOG.info("test {}", Supplier<Any> { "test" }, s2)
val s3: Supplier<*> = Supplier { t }
LOG.info("test {}", Supplier<Any> { "test" }, s3)
LOG.info("test {}", Supplier<Any> { "test" }, Supplier<Any> { RuntimeException() })
LOG.info("test {}", Supplier<Any> { "test" })
LOG.info(<warning descr="More arguments provided (2) than placeholders specified (1)">"test {}"</warning>, {""}, {" "})
LOG.info("test {}", {""}, {RuntimeException()})
val function: () -> String = { "RuntimeException()" }
LOG.info(<warning descr="More arguments provided (2) than placeholders specified (1)">"test {}"</warning>, {""}, function)
val function2: () -> RuntimeException = { RuntimeException() }
LOG.info("test {}", {""}, function2)
}
}
companion object {
private val LOG = LogManager.getLogger()
}
}
""".trimIndent())
}
fun `test error type`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
class Log4j {
fun m() {
var e = <error descr="[UNRESOLVED_REFERENCE] Unresolved reference: Ce">Ce</error>;
LOG.error(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"1 {} {} {}"</warning> , <error descr="[DEBUG] Resolved to error element">e</error>, <error descr="[DEBUG] Resolved to error element">e</error>)
}
companion object {
val LOG = LogManager.getLogger()
}
}
""".trimIndent())
}
fun `test formatted log4j with partial known strings`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
internal object Logging {
private val logger = LogManager.getFormatterLogger()
fun test(t: String) {
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (at least 1)">"%s" + t + 1 + "%s "</warning> )
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (at least 2)">"%s %s" + t + 1</warning> )
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (2)">"%s %s"</warning>, 1)
logger.atDebug().log("%s t", 1)
val logBuilder = logger.atDebug()
logBuilder.log(<warning descr="More arguments provided (2) than placeholders specified (0)">"%s t"</warning>, 1, 2)
logger.info(<warning descr="More arguments provided (2) than placeholders specified (1)">"%s t"</warning>, 2, 2)
logger.info("%s t", 2)
logger.info(<warning descr="More arguments provided (2) than placeholders specified (1)">"%s t"</warning>, 2, 3)
}
}
""".trimIndent())
}
fun `test log4j with partial known strings`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
internal object Logging {
private val logger = LogManager.getLogger()
fun test(t: String) {
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (at least 2)">"{}" + t + 1 + "{}"</warning> )
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (at least 2)">"{}" + t + 1 + "{}"</warning> , RuntimeException())
logger.info( <warning descr="Fewer arguments provided (1) than placeholders specified (at least 3)">"{} {}" + t + 1 + "{}"</warning> , 1, RuntimeException())
logger.info("{}" + t + 1 + "{}", 1, RuntimeException())
logger.info("{}" + t + 1 + "{}", 1, 1)
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,206 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspection
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionPlaceholderNumberTest : LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase(), KotlinPluginModeProvider {
fun `test many variables`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
var LOGGER = LoggerFactory.getLogger()
fun m() {
val a1 = " {} " +" s"
val a2 = a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1 + a1
LOGGER.info( <warning descr="Fewer arguments provided (1) than placeholders specified (at least 3)">"abd"+a2</warning> , 1)
}
}
""".trimIndent())
}
fun `test variables`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
private const val con = "{}"
internal class X {
var logger = LoggerFactory.getLogger()
private var con2 = "{}"
private fun slf4jVariable3(number: String) {
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">number + "1 {} {}"</warning>, 1) //warn
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">number + "1 {} {}"</warning>, 1) //warn
var t = "{}"
logger.info(con2 + t + "1", 1)
logger.info(con2 + t + "1", 1)
}
private fun slf4jVariable(number: String) {
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">number + "1 {} {}"</warning>, 1) //warn
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">number + "1 {} {}"</warning>, 1) //warn
var t = "{}"
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (2)">con + t + "1"</warning>, 1) //warn
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (2)">con + t + "1"</warning>, 1) //warn
}
private fun slf4jVariable2(number: String) {
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">number + "1 {} {}"</warning>, 1) //warn
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">number + "1 {} {}"</warning>, 1) //warn
var t = "{}"
logger.info(con + t + "1", 1)
t = "1"
logger.info(con + t + "1", 1)
}
}
""".trimIndent())
}
fun `test fewer and more placeholders`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.*;
internal class X {
fun foo() {
val logger = LoggerFactory.getLogger()
logger.info( <warning descr="Fewer arguments provided (1) than placeholders specified (2)">"string {}{}"</warning> , 1)
logger.info( <warning descr="More arguments provided (1) than placeholders specified (0)">"string"</warning> , 1)
}
}
""".trimIndent())
}
fun `test escaping`() {
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
var LOG = LoggerFactory.getLogger()
fun m() {
LOG.info("Created key {}\\\\{}", 1, 2)
LOG.info( <warning descr="More arguments provided (2) than placeholders specified (1)">"Created key {}\\{}"</warning> , 1, 2)
}
}
""".trimIndent())
}
fun `test non constant string`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
val LOG = LoggerFactory.getLogger()
fun m() {
LOG.info( <warning descr="Fewer arguments provided (0) than placeholders specified (4)">S + "{}" + (1 + 2) + '{' + '}' + S</warning> )
LOG.info( <warning descr="Fewer arguments provided (0) than placeholders specified (1)">message</warning> )
}
companion object {
private const val message = "HELLO {}"
private const val S = "{}"
}
}
""".trimIndent())
}
fun `test 1 exception and several placeholder`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
fun foo() {
val e = RuntimeException()
LoggerFactory.getLogger().info( <warning descr="Fewer arguments provided (0) than placeholders specified (1)">"this: {}"</warning> , e)
LoggerFactory.getLogger().info("1: {} e: {}", 1, e)
LoggerFactory.getLogger().info( <warning descr="Fewer arguments provided (1) than placeholders specified (3)">"1: {} {} {}"</warning> , 1, e)
val logger = LoggerFactory.getLogger()
logger.info("string {}", 1)
}
}
""".trimIndent())
}
fun `test throwable`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
fun foo() {
val logger = LoggerFactory.getLogger()
logger.info("string {}", 1, RuntimeException())
}
}
""".trimIndent())
}
fun `test multi catch`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
fun multiCatch() {
try {
method()
} catch (e: FirstException) {
logger.info("failed with first or second", e)
} catch (e: SecondException) {
logger.info("failed with first or second", e)
}
}
fun method() {
}
class FirstException : Exception()
class SecondException : Exception()
companion object {
private val logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test no slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
internal class FalsePositiveSLF4J {
fun method(definitelyNotSLF4J: DefinitelyNotSLF4J) {
definitelyNotSLF4J.info("not a trace message", "not a trace parameter")
}
interface DefinitelyNotSLF4J {
fun info(firstParameter: String?, secondParameter: Any?)
}
}
""".trimIndent())
}
fun `test array argument`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
var LOG = LoggerFactory.getLogger()
fun m(a: String, b: Int, c: Any) {
LOG.info("test {} for test {} in test {}", *arrayOf(a, b, c))
}
}
""".trimIndent())
}
fun `test uncountable array`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
var LOG = LoggerFactory.getLogger()
fun m(objects: Array<Any?>) {
LOG.info("test {} test text {} text {}", *objects)
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,167 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspection
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionSlf4JTest : LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase(), KotlinPluginModeProvider {
fun `test slf4j disable slf4jToLog4J2Type`() {
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
fun foo(s: String) {
val logger = LoggerFactory.getLogger()
logger.info( <warning descr="Fewer arguments provided (1) than placeholders specified (2)">"string {} {}"</warning> , 1, RuntimeException())
logger.info(<warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">s + "string {} {}"</warning> , 1, RuntimeException())
logger.info(s + "string {} {}" , 1, 2)
logger.atError().log( <warning descr="Fewer arguments provided (0) than placeholders specified (1)">"{}"</warning> , RuntimeException("test"))
LoggerFactory.getLogger().atError().log( <warning descr="Fewer arguments provided (1) than placeholders specified (2)">"{} {}"</warning> , 1, RuntimeException("test"))
LoggerFactory.getLogger().atError().log("{}", 1, RuntimeException("test"))
LoggerFactory.getLogger().atError().log(s + "{}", 1, RuntimeException("test"))
LoggerFactory.getLogger().atError().log(s + "{}", 1, 2)
LoggerFactory.getLogger().atError().log(<warning descr="More arguments provided (1) than placeholders specified (0)">""</warning>, 1, RuntimeException("test"))
LoggerFactory.getLogger().atError().log(<warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">s + "{} {} {}"</warning>, 1, RuntimeException("test"))
}
}
""".trimIndent())
}
fun `test slf4j auto slf4jToLog4J2Type`() {
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.AUTO
myFixture.addClass("""
package org.apache.logging.slf4j;
public interface Log4jLogger {
}
""".trimIndent())
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
internal class X {
fun foo() {
val logger = LoggerFactory.getLogger()
logger.info("string {} {}", 1, RuntimeException())
logger.atError().log("{}", RuntimeException("test"))
LoggerFactory.getLogger().atError().log("{} {}", 1, RuntimeException("test"))
LoggerFactory.getLogger().atError().log( <warning descr="More arguments provided (2) than placeholders specified (1)">"{}"</warning> , 1, RuntimeException("test"))
}
}
""".trimIndent())
}
fun `test slf4j`() {
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.*;
private val logger: Logger? = null
private val brackets: String = "{}"
fun foo() {
logger?.debug(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.debug("test {} {}", *arrayOf(1, 2))
logger?.debug("test {} {}", *arrayOf(1, 2, Exception()))
logger?.debug(<warning descr="More arguments provided (2) than placeholders specified (1)">"test " + brackets</warning>, 1, 2) //warn
logger?.debug("test {}" + brackets, 1, 2)
logger?.debug("test {} {}", 1, 2)
logger?.debug(<warning descr="Fewer arguments provided (1) than placeholders specified (2)">"test {} \\{} {}"</warning>, 1) //warn
logger?.debug("test {} \\{} {}", 1, 2)
logger?.debug(<warning descr="Fewer arguments provided (1) than placeholders specified (2)">"test {} {}"</warning>, 1, Exception()) //warn
logger?.debug("test {} {}", 1, 2, Exception())
logger?.debug(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.debug(<warning descr="More arguments provided (2) than placeholders specified (1)">"test {}"</warning>, 1, 2) //warn
logger?.error(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.info(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.trace(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
logger?.warn(<warning descr="Fewer arguments provided (2) than placeholders specified (3)">"test {} {} {}"</warning>, 1, 2) //warn
}
""".trimIndent())
}
fun `test slf4j with partial known strings`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
import java.util.*
internal class X {
var logger = LoggerFactory.getLogger()
fun m(t: String) {
logger.info("{} {}", 1, 2)
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (at least 2)">"{}" + t + 1 + "{}"</warning> )
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (at least 1)">"{}" + t + 1</warning> )
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (at least 1)">"{}${'$'}t{}"</warning> )
logger.info("{}${'$'}t{}", 1, 2)
logger.info("{}${'$'}t{}", 1, 2, 3)
val temp1 = "{} {}${'$'}t"
var temp = "{} {}${'$'}t"
logger.info( <warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">temp1</warning> , 1)
logger.info(temp, 1, 2, 3)
logger.info(logText, 1, 2, 3)
logger.info( <warning descr="Fewer arguments provided (1) than placeholders specified (at least 2)">logText</warning> , 1)
logger.info( <warning descr="Fewer arguments provided (1) than placeholders specified (at least 3)">logText2</warning> , 1)
logger.info( <warning descr="Fewer arguments provided (1) than placeholders specified (3)">logText3</warning> , 1)
temp = "{}${'$'}t"
logger.info(temp, 1)
}
fun m(i: Int, s: String) {
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (1)">"test1 {}"</warning> )
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (at least 1)">"test1 {}${'$'}s"</warning> )
logger.info( <warning descr="Fewer arguments provided (0) than placeholders specified (1)">"test1 {}${'$'}i"</warning> )
}
companion object {
private val logText = "{} {}" + something
private val logText2 = "{} {}" + 1 + "{}" + something
private const val logText3 = "{} {}" + 1 + "{}"
private val something: String
get() = if (Random().nextBoolean()) "{}" else ""
}
}
""".trimIndent())
}
fun `test slf4j builder`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
import org.slf4j.spi.LoggingEventBuilder
fun foo() {
LoggerFactory.getLogger().atError().log("{}", RuntimeException("test"))
LoggerFactory.getLogger().atError().log("{} {}", 1, RuntimeException("test"))
LoggerFactory.getLogger().atError().log( <warning descr="More arguments provided (2) than placeholders specified (1)">"{}"</warning> , 1, RuntimeException("test"))
logger2.atError().log(<warning descr="Fewer arguments provided (1) than placeholders specified (2)">"{} {}"</warning>, 1)
val loggingEventBuilder = logger2.atError()
loggingEventBuilder
.log("{} {}", 2) //skip, because it can be complex cases
logger2.atError()
.log(<warning descr="Fewer arguments provided (1) than placeholders specified (2)">"{} {}"</warning>, 2) //warn
logger2.atError()
.addArgument("s")
.addKeyValue("1", "1")
.log("{} {}", 2)
logger2.atError()
.setMessage(<warning descr="Fewer arguments provided (0) than placeholders specified (2)">"{} {}"</warning>)
.log()
logger2.atError()
.addArgument("")
.addArgument("")
.setMessage("{} {}")
.log()
}
private val logger2 = LoggerFactory.getLogger()
private val builder: LoggingEventBuilder = logger2.atError()
""".trimIndent())
}
}

View File

@@ -0,0 +1,116 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingSimilarMessageInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinLoggingSimilarMessageInspectionTest : LoggingSimilarMessageInspectionTestBase(), KotlinPluginModeProvider {
fun `test equals log4j2`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
internal class Logging {
private val logger: Logger = LogManager.getLogger()
private fun request1(i: String) {
val msg = "log messages: {}"
logger.<weak_warning descr="Similar log messages">info(msg, i)</weak_warning>
}
private fun request2(i: Int) {
val msg = "log messages: {}"
logger.<weak_warning descr="Similar log messages">info(msg, i)</weak_warning>
}
}
""".trimIndent())
}
fun `test equals slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class Logging {
private val LOG: Logger = LoggerFactory.getLogger(Logging::class.<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: java">java</error>)
private fun request1(i: String) {
val msg = "log messages: {}"
LOG.<weak_warning descr="Similar log messages">info(msg, i)</weak_warning>
LOG.info("1" + msg, i)
}
private fun request2(i: Int) {
val msg = "log messages: {}"
LOG.<weak_warning descr="Similar log messages">info(msg, i)</weak_warning>
}
}
""".trimIndent())
}
fun `test setMessage slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class Logging {
private val LOG: Logger = LoggerFactory.getLogger(Logging::class.<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: java">java</error>)
private fun request1(i: String) {
val msg = "log messages: {}" + i
LOG.atInfo().setCause(RuntimeException()).setMessage(msg).log()
LOG.atInfo().setMessage(msg).<weak_warning descr="Similar log messages">log()</weak_warning>
}
private fun request2(i: Int) {
val msg = "log messages: {}" + i
LOG.atInfo().setCause(RuntimeException()).setMessage(msg).log()
LOG.atInfo().setMessage(msg).<weak_warning descr="Similar log messages">log()</weak_warning>
}
}
""".trimIndent())
}
fun `test skip in the middle parts`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class Logging {
private val LOG: Logger = LoggerFactory.getLogger(Logging::class.<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: java">java</error>)
private fun request1(i: String) {
val msg = "${"\${i}"}1234356${"\${i}"}"
LOG.info(msg)
}
private fun request2(i: Int) {
val msg = "something 1234356${"\${i}"}"
LOG.info(msg)
}
}
""".trimIndent())
}
fun `test skip inside calls`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class Logging {
private val LOG: Logger = LoggerFactory.getLogger(Logging::class.<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: java">java</error>)
private fun request2() {
LOG.warn("Non-cached operation ${"\${operationName(\"update\")}"}")
LOG.warn("Non-cached operation ${"\${operationName(\"getChildren\")}"}")
}
private fun operationName(operationName: String): String {
return operationName
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,330 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingStatementNotGuardedByLogConditionInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinLoggingStatementNotGuardedByLogConditionInspectionTest : LoggingStatementNotGuardedByLogConditionInspectionTestBase(), KotlinPluginModeProvider {
fun `test slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
private val LOG = LoggerFactory.getLogger()
fun n(arg: String) {
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test {}", arg)</warning>
}
}
""".trimIndent())
}
fun `test log4j2`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
internal class X {
fun n(arg: String) {
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test {}", arg)</warning>
}
companion object {
val LOG: Logger = LogManager.getLogger()
}
}
""".trimIndent())
}
fun `test skip according level for slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
LOG.warn("test " + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test is surrounded by guard for slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.isDebugEnabled) {
LOG.debug("test " + arg)
}
if (LOG.isInfoEnabled) {
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test" + arg)</warning> //todo!
}
if (true && LOG.isDebugEnabled) {
LOG.debug("test {}", arg)
}
if (true && LOG.isDebugEnabled) {
if (true) {
LOG.debug("test {}", arg)
}
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test skip if only constant arguments for slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n() {
LOG.debug("test")
LOG.debug("test {} {}", "test" + "test", 1 + 1)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test don't skip if only constant arguments for slf4j flagUnguardedConstant`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n1() {
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test")</warning>
}
fun n2() {
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test")</warning>
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test skip with several log calls for slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n2(arg: String) {
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test1" + arg)</warning>
LOG.debug("test2" + arg)
}
fun n3(arg: String) {
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test1" + arg)</warning>
LOG.debug("test2" + arg)
LOG.debug("test2" + arg)
}
fun constantCall(arg: String) {
LOG.debug("test1")
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test2" + arg)</warning>
}
fun beforeNotLog(arg: String) {
constantCall(arg)
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test2" + arg)</warning>
}
fun differentLevels(arg: String) {
<warning descr="Logging call not guarded by a logging condition">LOG.debug("test1" + arg)</warning>
LOG.warn("test2" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent())
}
fun `test fix simple slf4j`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
LOG.<caret>debug("test" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.isDebugEnabled) {
LOG.debug("test" + arg)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.log.statement.not.guarded.log.fix.family.name")
)
}
fun `test fix simple nested slf4j`() {
myFixture.testQuickFix(
testPreview = false,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if(true){
LOG.<caret>debug("test" + arg)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if(true){
if (LOG.isDebugEnabled) {
LOG.debug("test" + arg)
}
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.log.statement.not.guarded.log.fix.family.name")
)
}
fun `test fix several similar slf4j`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
LOG.<caret>debug("test1" + arg)
LOG.debug("test2" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.isDebugEnabled) {
LOG.debug("test1" + arg)
LOG.debug("test2" + arg)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.log.statement.not.guarded.log.fix.family.name")
)
}
fun `test fix several different slf4j`() {
myFixture.testQuickFix(
testPreview = true,
lang = JvmLanguage.KOTLIN,
before = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
LOG.<caret>debug("test1" + arg)
LOG.debug("test2" + arg)
LOG.info("test3" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
after = """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class X {
fun n(arg: String) {
if (LOG.isDebugEnabled) {
LOG.debug("test1" + arg)
LOG.debug("test2" + arg)
}
LOG.info("test3" + arg)
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger()
}
}
""".trimIndent(),
hint = JvmAnalysisBundle.message("jvm.inspection.log.statement.not.guarded.log.fix.family.name")
)
}
}

View File

@@ -0,0 +1,86 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.codeInspection.logging.LoggingStringTemplateAsArgumentInspection
import com.intellij.codeInspection.logging.LoggingUtil
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingStringTemplateAsArgumentInspectionTestBase
import com.intellij.testFramework.TestDataPath
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
private const val INSPECTION_PATH = "/codeInspection/logging/stringTemplateAsArgument"
@TestDataPath("\$CONTENT_ROOT/testData$INSPECTION_PATH")
abstract class KotlinLoggingStringTemplateAsArgumentInspectionTest : LoggingStringTemplateAsArgumentInspectionTestBase(), KotlinPluginModeProvider {
override val inspection: LoggingStringTemplateAsArgumentInspection = LoggingStringTemplateAsArgumentInspection().apply {
myLimitLevelType = LoggingUtil.LimitLevelType.ALL
}
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + INSPECTION_PATH
private fun withSettings(
skipPrimitives: Boolean = inspection.mySkipPrimitives,
levelType: LoggingUtil.LimitLevelType = inspection.myLimitLevelType,
skipWithTheOnlyException: Boolean = inspection.mySkipWithTheOnlyException,
test: () -> Unit
) {
val curSkipPrimitives = inspection.mySkipPrimitives
val curLimitLevelType = inspection.myLimitLevelType
val curSkipWithTheOnlyException = inspection.mySkipWithTheOnlyException
try {
inspection.mySkipPrimitives = skipPrimitives
inspection.myLimitLevelType = levelType
inspection.mySkipWithTheOnlyException = skipWithTheOnlyException
test()
} finally {
inspection.mySkipPrimitives = curSkipPrimitives
inspection.myLimitLevelType = curLimitLevelType
inspection.mySkipWithTheOnlyException = curSkipWithTheOnlyException
}
}
fun `test default settings highlighting, with guards`() {
myFixture.testHighlighting("StringTemplateAsArgumentGuarded.kt")
}
fun `test highlighting info and lower no skip primitives`() {
withSettings(skipPrimitives = false, levelType = LoggingUtil.LimitLevelType.INFO_AND_LOWER) {
myFixture.testHighlighting("StringTemplateAsArgumentWarnInfo.kt")
}
}
fun `test highlighting debug no skip primitives`() {
withSettings(skipPrimitives = false, LoggingUtil.LimitLevelType.DEBUG_AND_LOWER) {
myFixture.testHighlighting("StringTemplateAsArgumentWarnDebug.kt")
}
}
fun `test highlighting all skip primitives`() {
withSettings(skipPrimitives = true, LoggingUtil.LimitLevelType.ALL) {
myFixture.testHighlighting("StringTemplateAsArgumentSkipPrimitives.kt")
}
}
fun `test highlighting all no skip primitives`() {
withSettings(skipPrimitives = false, LoggingUtil.LimitLevelType.ALL) {
myFixture.testHighlighting("StringTemplateAsArgument.kt")
}
}
fun `test fix all no skip primitives`() {
withSettings(skipPrimitives = false, LoggingUtil.LimitLevelType.ALL) {
myFixture.testQuickFix("StringTemplateAsArgumentFix.kt", checkPreview = true)
}
}
fun `test fix with escape symbol all no skip primitives`() {
withSettings(skipPrimitives = false, LoggingUtil.LimitLevelType.ALL) {
myFixture.testQuickFix("StringTemplateAsArgumentWithEscapeSymbolsFix.kt", checkPreview = true)
}
}
fun `test highlighting all no skip primitives skip with the only exception`() {
withSettings(skipPrimitives = false, LoggingUtil.LimitLevelType.ALL, skipWithTheOnlyException = true) {
myFixture.testHighlighting("StringTemplateAsArgumentSkipException.kt")
}
}
}

View File

@@ -0,0 +1,98 @@
package com.intellij.codeInspection.tests.kotlin.performance
import com.intellij.jvm.analysis.internal.testFramework.performance.UrlHashCodeInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinUrlHashCodeInspectionTest : UrlHashCodeInspectionTestBase(), KotlinPluginModeProvider {
fun `test url hashcode call`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.net.URL
class UrlHashCode {
@Suppress("DEPRECATION")
fun foo() {
val url = URL("")
url.<warning descr="Call to 'hashCode()' on URL object">hashCode</warning>()
}
}
""".trimIndent())
}
fun `test url equals call`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.net.URL
class UrlHashCodeEquals {
@Suppress("DEPRECATION")
fun foo() {
val url1 = URL("")
val url2 = URL("")
url1 <warning descr="Call to 'equals()' on URL object">==</warning> url2
url1.<warning descr="Call to 'equals()' on URL object">equals</warning>(url2)
}
}
""".trimIndent())
}
fun `test url variable with URL maps or sets`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.net.URL
import java.util.*
class CollectionContainsUrl {
val strMap: Map<String, String> = HashMap()
val <warning descr="'urlMap' may contain URL objects">urlMap</warning>: Map<URL, String> = HashMap()
val <warning descr="'urlMapOfMap' may contain URL objects">urlMapOfMap</warning>: Map<String, Map<URL, String>> = HashMap()
val <warning descr="'urlSet' may contain URL objects">urlSet</warning>: Set<URL> = HashSet()
val <warning descr="'urlSetOfMap' may contain URL objects">urlSetOfMap</warning>: Set<Map<URL, String>> = HashSet()
}
""".trimIndent())
}
fun `test url URL map operations`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.net.URL
import java.util.*
class CollectionContainsUrl {
val objMap: MutableMap<Any, Any> = HashMap()
@Suppress("DEPRECATION")
fun foo() {
<warning descr="'objMap' may contain URL objects">objMap</warning>.put(URL(""), "")
}
}
""".trimIndent())
}
fun `test url URL set operations`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.net.URL
import java.util.*
class CollectionContainsUrl {
val objSet: MutableSet<Any> = HashSet()
@Suppress("DEPRECATION")
fun foo() {
<warning descr="'objSet' may contain URL objects">objSet</warning>.add(URL(""))
}
}
""".trimIndent())
}
fun `test URL doesn't highlight when comparing with null`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import java.net.URL
@Suppress("DEPRECATION", "SENSELESS_COMPARISON")
fun main() {
val sample = URL("")
if (sample == null) {}
if (null == sample) {}
sample.equals(null)
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,27 @@
package com.intellij.codeInspection.tests.kotlin.sourceToSink
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.jvm.analysis.internal.testFramework.sourceToSink.SourceToSinkFlowInspectionTestBase
import com.intellij.testFramework.TestDataPath
import org.jetbrains.annotations.Nls
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
private const val inspectionPath = "/codeInspection/sourceToSinkFlow/markAsSafeFix"
@TestDataPath("\$CONTENT_ROOT/testData$inspectionPath")
abstract class KotlinMarkAsSafeFixSourceToSinkFlowInspectionTest : SourceToSinkFlowInspectionTestBase(), KotlinPluginModeProvider {
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + inspectionPath
fun `test common cases with checkFramework`() {
prepareCheckFramework()
myFixture.testQuickFix("CommonCasesCheckFramework.kt", getMessage(), false)
}
fun `test common cases with jsr`() {
prepareJsr()
myFixture.testQuickFix("CommonCasesJsr.kt", getMessage(), false)
}
private fun getMessage(): @Nls String = JvmAnalysisBundle.message("jvm.inspections.source.unsafe.to.sink.flow.mark.as.safe.text")
}

View File

@@ -0,0 +1,185 @@
package com.intellij.codeInspection.tests.kotlin.sourceToSink
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.jvm.analysis.internal.testFramework.sourceToSink.SourceToSinkFlowInspectionTestBase
import com.intellij.testFramework.TestDataPath
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
private const val INSPECTION_PATH = "/codeInspection/sourceToSinkFlow"
@TestDataPath("\$CONTENT_ROOT/testData$INSPECTION_PATH")
abstract class KotlinSourceToSinkFlowInspectionTest : SourceToSinkFlowInspectionTestBase(), KotlinPluginModeProvider {
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + INSPECTION_PATH
fun testSimple() {
prepareCheckFramework()
myFixture.testHighlighting("Simple.kt")
}
fun testLocalInference() {
prepareCheckFramework()
myFixture.testHighlighting("LocalInference.kt")
}
fun testSink() {
prepareCheckFramework()
myFixture.testHighlighting("Sink.kt")
}
fun testSinkJsr() {
prepareJsr()
myFixture.testHighlighting("SinkJsr.kt")
}
fun testCall() {
prepareCheckFramework()
myFixture.testHighlighting("Call.kt")
}
fun testLocalVariable() {
prepareCheckFramework()
myFixture.testHighlighting("LocalVariables.kt")
}
fun testParameters() {
prepareCheckFramework()
myFixture.testHighlighting("Parameters.kt")
}
fun testEnumAnnotations() {
prepareCheckFramework()
myFixture.testHighlighting("EnumAnnotations.kt")
}
fun testFields() {
prepareCheckFramework()
myFixture.testHighlighting("Fields.kt")
}
fun testStructure() {
prepareCheckFramework()
myFixture.testHighlighting("Structure.kt")
}
fun testMethodPropagation() {
prepareCheckFramework()
myFixture.testHighlighting("MethodPropagation.kt")
}
fun testKotlinPropertyPropagateFix() {
prepareCheckFramework()
myFixture.configureByFile("Property.kt")
val propagateAction = myFixture.getAvailableIntention(
JvmAnalysisBundle.message("jvm.inspections.source.unsafe.to.sink.flow.propagate.safe.text"))!!
myFixture.launchAction(propagateAction)
myFixture.checkResultByFile("Property.after.kt")
}
fun testLimits() {
prepareCheckFramework()
myFixture.addClass("""
@SuppressWarnings({"FieldMayBeStatic", "StaticNonFinalField", "RedundantSuppression"})
public class Limit2 {
public final static String fromAnotherFile = "1";
public final static String fromAnotherFile2 = fromAnotherFile;
public final static String fromAnotherFile3 = fromMethod();
public static String fromAnotherFile4 = "1";
public final String fromAnotherFile5 = "1";
public final String fromAnotherFile6;
public Limit2() {
this.fromAnotherFile6 = "";
}
private static String fromMethod() {
return "null";
}
}
""".trimIndent())
myFixture.testHighlighting("Limits.kt")
}
fun testMethodsAsFields() {
prepareCheckFramework()
myFixture.addClass("""
public class MethodAsFields {
private static final String t = "1";
public String getT() {
return t;
}
}
""".trimIndent())
myFixture.testHighlighting("MethodsAsFields.kt")
}
fun testDifferentExpressions() {
prepareCheckFramework()
myFixture.testHighlighting("DifferentExpressions.kt")
}
fun testKotlinConstructorArguments() {
prepareCheckFramework()
myFixture.testHighlighting("KotlinConstructorArguments.kt")
}
fun testKotlinParameters() {
prepareCheckFramework()
myFixture.testHighlighting("KotlinParameters.kt")
}
fun testDropLocality() {
prepareCheckFramework()
myFixture.testHighlighting("DropLocality.kt")
}
fun `test forEachLoop`() {
prepareCheckFramework()
myFixture.testHighlighting("ForEachLoop.kt")
}
fun `test lambdaWithForEachLoop`() {
prepareCheckFramework()
myFixture.testHighlighting("LambdaWithForEachLoop.kt")
}
fun `test if`() {
prepareCheckFramework()
myFixture.testHighlighting("ifStatement.kt")
}
fun `test custom through tables`() {
inspection.untaintedParameterIndex.apply {
this.clear()
this.add("1")
}
inspection.untaintedParameterMethodClass.apply {
this.clear()
this.add("FromMethod")
}
inspection.untaintedParameterMethodName.apply {
this.clear()
this.add("sink")
}
inspection.taintedParameterIndex.apply {
this.clear()
this.add("0")
}
inspection.taintedParameterMethodClass.apply {
this.clear()
this.add("FromMethod")
}
inspection.taintedParameterMethodName.apply {
this.clear()
this.add("test")
}
inspection.setTaintedMethod("java.lang.String", "toString")
inspection.setUntaintedMethod("java.lang.String", "trim")
myFixture.testHighlighting("FromMethod.kt")
}
}

View File

@@ -0,0 +1,143 @@
package com.intellij.codeInspection.tests.kotlin.test
import com.intellij.jvm.analysis.internal.testFramework.test.AssertEqualsBetweenInconvertibleTypesInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ContentEntry
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.PsiTestUtil
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
import java.io.File
abstract class KotlinAssertEqualsBetweenInconvertibleTypesInspectionTest : AssertEqualsBetweenInconvertibleTypesInspectionTestBase(), KotlinPluginModeProvider {
override fun getProjectDescriptor(): LightProjectDescriptor = object : AssertJProjectDescriptor(LanguageLevel.HIGHEST) {
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
super.configureModule(module, model, contentEntry)
val jar = File(PathUtil.getJarPathForClass(JvmStatic::class.java))
PsiTestUtil.addLibrary(model, "kotlin-stdlib", jar.parent, jar.name)
}
}
fun `test AssertJ incompatible types`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
class AssertEqualsBetweenInconvertibleTypes {
@org.junit.jupiter.api.Test
fun myTest() {
assertThat(1).<warning descr="'isSameAs()' between objects of inconvertible types 'int' and 'String'">isSameAs</warning>("foo")
Assertions.assertThat("foo").<warning descr="'isEqualTo()' between objects of inconvertible types 'String' and 'int'">isEqualTo</warning>(2)
Assertions.assertThat("foo").isEqualTo("bar") //ok
assertThat("foo").describedAs("foo").<warning descr="'isEqualTo()' between objects of inconvertible types 'String' and 'int'">isEqualTo</warning>(2)
Assertions.assertThat("foo").<warning descr="'isEqualTo()' between objects of inconvertible types 'String' and 'int'">isEqualTo</warning>(2)
Assertions.assertThat(1).<warning descr="'isSameAs()' between objects of inconvertible types 'int' and 'String'">isSameAs</warning>("foo")
Assertions.assertThat("foo").describedAs("foo").<warning descr="'isSameAs()' between objects of inconvertible types 'String' and 'int'">isSameAs</warning>(2)
assertThat(IntArray(2)).`as`("array").<warning descr="'isSameAs()' between objects of inconvertible types 'int[]' and 'int'">isSameAs</warning>(2)
Assertions.assertThat("foo").`as`("foo").<warning descr="'isEqualTo()' between objects of inconvertible types 'String' and 'int'">isEqualTo</warning>(2)
}
}
""".trimIndent())
}
// TODO type is not displayed correctly here, something goes wrong with KT to Java type converter
fun `_test AssertJ single element wrong type`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.assertj.core.api.Assertions
class MyTest {
@org.junit.jupiter.api.Test
fun testSingleElement() {
Assertions.assertThat(listOf(1))
.singleElement()
.<warning descr="'isEqualTo()' between objects of inconvertible types 'Integer' and 'String'">isEqualTo</warning>("1")
}
}
""".trimIndent())
}
// TODO type is not displayed correctly here, something goes wrong with KT to Java type converter
fun `_test AssertJ is equal to null`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.assertj.core.api.Assertions
class MyTest {
fun myNullable(): MyTest? = null
@org.junit.jupiter.api.Test
fun testExtractingNoHighlight() {
Assertions.assertThat(myNullable()).isEqualTo(null)
}
}
""".trimIndent())
}
// Fails on TC for unknown reason
fun `_test AssertJ single element extracting type mismatch`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.assertj.core.api.Assertions
class MyTest {
@org.junit.jupiter.api.Test
fun testExtractingNoHighlight() {
Assertions.assertThat(listOf(1))
.singleElement()
.extracting(Any::toString)
.<warning descr="'isEqualTo()' between objects of inconvertible types 'String' and 'int'">isEqualTo</warning>(1)
}
}
""".trimIndent())
}
// Fails on TC for unknown reason
fun `_test AssertJ extracting single element type mismatch`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.assertj.core.api.Assertions
class MyTest {
@org.junit.jupiter.api.Test
fun testExtractingNoHighlight() {
Assertions.assertThat(listOf(1))
.extracting(Any::toString)
.singleElement()
.<warning descr="'isEqualTo()' between objects of inconvertible types 'String' and 'int'">isEqualTo</warning>(1)
}
}
""".trimIndent())
}
fun `test AssertJ extracting as method reference type match`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.assertj.core.api.Assertions
class MyTest {
@org.junit.jupiter.api.Test
fun testExtractingNoHighlight() {
Assertions.assertThat(1)
.describedAs("Mapping to String")
.extracting(Any::toString)
.isEqualTo("1")
}
}
""".trimIndent())
}
fun `test AssertJ extracting as lambda type match`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.assertj.core.api.Assertions
class MyTest {
@org.junit.jupiter.api.Test
fun testExtractingNoHighlight() {
Assertions.assertThat(1)
.describedAs("Mapping to String")
.extracting { value -> "${"$"}value" }
.isEqualTo("1")
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,85 @@
package com.intellij.codeInspection.tests.kotlin.test
import com.intellij.jvm.analysis.internal.testFramework.test.TestCaseWithConstructorInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ContentEntry
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.pom.java.LanguageLevel
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.PsiTestUtil
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
import java.io.File
abstract class KotlinTestCaseWithConstructorInspectionTest : TestCaseWithConstructorInspectionTestBase(), KotlinPluginModeProvider {
override fun getProjectDescriptor(): LightProjectDescriptor = object : JUnitProjectDescriptor(LanguageLevel.HIGHEST) {
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
super.configureModule(module, model, contentEntry)
val jar = File(PathUtil.getJarPathForClass(JvmStatic::class.java))
PsiTestUtil.addLibrary(model, "kotlin-stdlib", jar.parent, jar.name)
}
}
fun `test no highlighting parameterized test case`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters
@RunWith(Parameterized::class)
class ParameterizedTest(private val x: Int, private val y: Int) {
@Test
public fun testMe() { }
companion object {
@JvmStatic
@Parameterized.Parameters
public fun parameters(): Array<Array<Any>> = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
}
}
""".trimIndent(), "ParameterizedTest")
}
fun `test no highlighting trivial constructor`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import junit.framework.TestCase
class TestCaseWithConstructorInspection2() : TestCase() {
constructor(x: Int) : this() {
if (false) {
println(x)
}
}
}
""".trimIndent(), "TestCaseWithConstructorInspection2")
}
fun `test highlighting simple non-trivial constructor`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import junit.framework.TestCase
class TestCaseWithConstructorInspection1() : TestCase() {
<warning descr="Initialization logic in constructor 'constructor()' instead of 'setup()' life cycle method">constructor</warning>(x: Int) : this() {
println(x)
}
}
""".trimIndent(), "TestCaseWithConstructorInspection1")
}
fun `test highlighting Junit 4`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
public class JUnit4TestCaseWithConstructor {
<warning descr="Initialization logic in constructor 'constructor()' instead of 'setup()' life cycle method">constructor</warning>() {
println()
println()
println()
}
@org.junit.Test
public fun testMe() {}
}
""".trimIndent(), "JUnit4TestCaseWithConstructor")
}
}

View File

@@ -0,0 +1,67 @@
package com.intellij.codeInspection.tests.kotlin.test
import com.intellij.jvm.analysis.internal.testFramework.test.TestCaseWithoutTestsInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinTestCaseWithoutTestsInspectionTest : TestCaseWithoutTestsInspectionTestBase(), KotlinPluginModeProvider {
fun `test case without test methods`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class <warning descr="Test class 'TestCaseWithNoTestMethods' has no tests">TestCaseWithNoTestMethods</warning> : junit.framework.TestCase() {
override fun setUp() {
super.setUp()
}
override fun tearDown() {
super.tearDown()
}
fun testOne(): Int {
return 1
}
private fun testThree() { }
fun testFour(i: Int) { i + 1 }
}
""".trimIndent())
}
fun `test case with JUnit 3 inner class without test methods`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class <warning descr="Test class 'TestCaseWithInner' has no tests">TestCaseWithInner</warning> : junit.framework.TestCase() {
class Inner : junit.framework.TestCase() {
fun test1() {}
}
}
""".trimIndent())
}
fun `test case with JUnit 5 nested class without test methods`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
class <warning descr="Test class 'TestCaseWithInner' has no tests">TestCaseWithInner</warning> {
@org.junit.jupiter.api.Nested
inner class <warning descr="Test class 'Inner' has no tests">Inner</warning> {
private fun test1() { }
}
}
""".trimIndent())
}
fun `test case without test methods but class is ignored`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
@org.junit.Ignore
class IgnoredTest : junit.framework.TestCase() { }
""".trimIndent())
}
fun `test case with test in parent class`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
open class SomeParentClass(val name: String) : junit.framework.TestCase() {
fun testInParent() { }
}
class SomeTestClass : SomeParentClass("") { }
""".trimIndent())
}
}

View File

@@ -0,0 +1,82 @@
package com.intellij.codeInspection.tests.kotlin.test
import com.intellij.jvm.analysis.internal.testFramework.test.TestFailedLineInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinTestFailedLineInspectionTest : TestFailedLineInspectionTestBase(), KotlinPluginModeProvider {
fun `test non qualified call`() {
doTest(
lang = JvmLanguage.KOTLIN,
text = """
class MainTest : junit.framework.TestCase() {
fun testFoo() {
<warning descr="junit.framework.AssertionFailedError:">assertEquals</warning>()
assertEquals()
}
fun assertEquals() {}
}
""".trimIndent(),
url = "java:test://MainTest/testFoo",
errorMessage = "junit.framework.AssertionFailedError:",
stackTrace = """
|${'\t'}at junit.framework.Assert.fail(Assert.java:47)
|${'\t'}at MainTest.assertEquals(Assert.java:207)
|${'\t'}at MainTest.testFoo(MainTest.kt:3)
""".trimMargin()
)
}
fun `test qualified call`() {
doTest(
lang = JvmLanguage.KOTLIN,
text = """
class QualifiedTest : junit.framework.TestCase() {
fun testFoo() {
Assertions.<warning descr="junit.framework.AssertionFailedError:">assertEquals</warning>()
}
object Assertions {
fun assertEquals() {}
}
}
""".trimIndent(),
url = "java:test://QualifiedTest/testFoo",
errorMessage = "junit.framework.AssertionFailedError:",
stackTrace = """
|${'\t'}at junit.framework.Assert.fail(Assert.java:47)
|${'\t'}at QualifiedTest.assertEquals(Assert.java:207)
|${'\t'}at QualifiedTest.testFoo(QualifiedTest.kt:3)
""".trimMargin()
)
}
fun `test local method call`() {
doTest(
lang = JvmLanguage.KOTLIN,
text = """
class LocalFunctionTest {
@org.junit.jupiter.api.Test
fun testFoo() {
fun doTest() {
org.junit.jupiter.api.Assertions.assertEquals("expected", "actual")
}
<warning descr="org.opentest4j.AssertionFailedError:">doTest</warning>()
}
}
""".trimIndent(),
url = "java:test://LocalFunctionTest/testFoo",
errorMessage = "org.opentest4j.AssertionFailedError:",
stackTrace = """
|${'\t'}at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
|${'\t'}at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
|${'\t'}at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
|${'\t'}at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
|${'\t'}at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1141)
|${'\t'}at LocalFunctionTest.testFoo${'$'}doTest(LocalFunctionTest.kt:5)
|${'\t'}at LocalFunctionTest.testFoo(LocalFunctionTest.kt:7)
""".trimMargin()
)
}
}

View File

@@ -0,0 +1,130 @@
package com.intellij.codeInspection.tests.kotlin.test
import com.intellij.jvm.analysis.internal.testFramework.test.TestMethodWithoutAssertionInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ContentEntry
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.pom.java.LanguageLevel
import com.intellij.project.IntelliJProjectConfiguration
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.PsiTestUtil
import com.intellij.util.PathUtil
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
import java.io.File
abstract class KotlinTestMethodWithoutAssertionInspectionTest : TestMethodWithoutAssertionInspectionTestBase(), KotlinPluginModeProvider {
override fun getProjectDescriptor(): LightProjectDescriptor = object : TestFrameworkDescriptor(LanguageLevel.HIGHEST) {
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
super.configureModule(module, model, contentEntry)
val stdLibJar = File(PathUtil.getJarPathForClass(JvmStatic::class.java))
PsiTestUtil.addLibrary(model, "kotlin-stdlib", stdLibJar.parent, stdLibJar.name)
val ktTestJar = File(IntelliJProjectConfiguration.getProjectLibraryClassesRootPaths("kotlin-test").first())
PsiTestUtil.addLibrary(model, "kotlin-test", ktTestJar.parent, ktTestJar.name)
}
}
fun `test highlighting for empty method body`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import junit.framework.TestCase
import org.junit.Test
import org.junit.Assert
class TestMethodWithoutAssertion : TestCase() {
public fun <warning descr="Test method 'test()' contains no assertions">test</warning>() { }
@Test
public fun <warning descr="Test method 'fourOhTest()' contains no assertions">fourOhTest</warning>() { }
@Test(expected = Exception::class)
public fun fourOhTestWithExpected() { }
}
""".trimIndent())
}
fun `test no highlighting when assertion is present`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import junit.framework.TestCase
import org.junit.Test
import org.junit.Assert
import mockit.*
class TestMethodWithoutAssertion : TestCase() {
@Test
public fun fourOhTest2() { Assert.assertTrue(true) }
public fun test2() { assertTrue(true) }
public fun test3() { fail() }
@Test
public fun delegateOnly() { check() }
@Test
public fun delegateAdditionally() {
val i = 9
println(i)
check()
}
private fun check() { Assert.assertTrue(true) }
@Test
public fun testExecuteReverseAcknowledgement(@Mocked messageDAO: Any) {
println(messageDAO)
object : Verifications() { }
}
@Test
@Throws(AssertionError::class)
public fun testMethodWhichThrowsExceptionOnFailure() {
if (true) throw AssertionError()
}
}
""".trimIndent())
}
fun `test no highlighting kotlin stdlib assertion`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.junit.Test
import kotlin.test.*
class TestMethodWithAssertion {
@Test
public fun ktPrecondition1() { assert(true) }
@Test
public fun ktTestAssertion1() { assertTrue(true) }
}
""".trimIndent())
}
fun `test no highlighting kotlin JUnit 5 assertion`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
class TestMethodWithAssertion {
@Test
fun testFoo() {
assertDoesNotThrow<IllegalStateException> { throw IllegalStateException() }
}
}
""".trimIndent())
}
fun `test no highlighting mockk assertion`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.junit.jupiter.api.Test
import io.mockk.verify
class TestMethodWithAssertion {
@Test
fun testFoo() {
verify { }
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,113 @@
package com.intellij.codeInspection.tests.kotlin.test
import com.intellij.jvm.analysis.internal.testFramework.test.TestOnlyInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider
abstract class KotlinTestOnlyInspectionTest : TestOnlyInspectionTestBase(), KotlinPluginModeProvider {
fun `test @TestOnly on use-site targets`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
package test
import org.jetbrains.annotations.TestOnly
import org.jetbrains.annotations.VisibleForTesting
// IDEA-269740 need better support for UAST properties
@get:[TestOnly VisibleForTesting]
val x = 0
@get:[TestOnly]
val y = 0
@get:TestOnly
val z = 0
fun doSomething(q: Int) = q
fun main() {
doSomething(<warning descr="Test-only method is called in production code">y</warning>)
doSomething(<warning descr="Test-only method is called in production code">z</warning>)
}
""".trimIndent())
}
fun `test @TestOnly in production code`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
package test
import org.jetbrains.annotations.TestOnly
import org.jetbrains.annotations.VisibleForTesting
class TestOnlyTest @TestOnly constructor() {
val nonTestField = 0
var aField = 0
@TestOnly get() = field
@TestOnly
fun aMethod(x: Int): Int = x
@TestOnly
@<warning descr="@VisibleForTesting makes little sense on @TestOnly code">VisibleForTesting</warning>
fun aStringMethod(): String = "Foo"
}
/**
* [TestOnlyTest.aMethod]
* [testOnly]
*/
fun main() {
val foo1 = <warning descr="Test-only class is referenced in production code">TestOnlyTest</warning>()
val foo2 = test.<warning descr="Test-only class is referenced in production code">TestOnlyTest</warning>()
val foo3 = <warning descr="Test-only class is referenced in production code">TestOnlyTest</warning>().nonTestField
val bar = foo1.<warning descr="Test-only method is called in production code">aField</warning>
foo1.<warning descr="Test-only method is called in production code">aMethod</warning>(bar)
TestOnlyTest::<warning descr="Test-only method is called in production code">aMethod</warning>.invoke(foo2, foo3)
test.TestOnlyTest::<warning descr="Test-only method is called in production code">aMethod</warning>.invoke(foo2, foo3)
<warning descr="Test-only method is called in production code">testOnly</warning>()
}
@TestOnly
fun testOnly() {
val foo1 = TestOnlyTest()
val foo2 = test.TestOnlyTest()
val foo3 = TestOnlyTest().nonTestField
val bar = foo1.aField
foo1.aMethod(bar)
TestOnlyTest::aMethod.invoke(foo2, foo3)
test.TestOnlyTest::aMethod.invoke(foo2, foo3)
}
""".trimIndent())
}
fun `test @VisibleForTesting in production code`() {
myFixture.addFileToProject("VisibleForTestingTestApi.kt", """
package testapi
import org.jetbrains.annotations.VisibleForTesting
object VisibleForTestingTestApi {
var foo = 0 @VisibleForTesting get() = field
@VisibleForTesting
fun bar() { }
}
""".trimIndent())
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.jetbrains.annotations.VisibleForTesting
import testapi.VisibleForTestingTestApi
object VisibleForTestingTest {
val foobar = 0
@VisibleForTesting get() = field
fun main() {
foobar
VisibleForTestingTestApi.<warning descr="Test-only method is called in production code">foo</warning>
VisibleForTestingTestApi.<warning descr="Test-only method is called in production code">bar</warning>()
}
}
""".trimIndent())
}
}