-
-
Notifications
You must be signed in to change notification settings - Fork 332
/
MockkUnnecessaryUsageDetector.kt
74 lines (63 loc) · 3.06 KB
/
MockkUnnecessaryUsageDetector.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package io.mockk.lint
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope.JAVA_FILE
import com.android.tools.lint.detector.api.Scope.TEST_SOURCES
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.TextFormat
import com.android.tools.lint.detector.api.isString
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTypesUtil
import com.intellij.psi.util.TypeConversionUtil.isPrimitive
import com.intellij.psi.util.TypeConversionUtil.isPrimitiveWrapper
import org.jetbrains.uast.UCallExpression
import java.util.*
internal class MockkUnnecessaryUsageDetector : Detector(), SourceCodeScanner {
override fun getApplicableMethodNames() = listOf("mockk")
private fun PsiMethod.isInPackageName() = (containingFile as? PsiJavaFile)?.packageName == "io.mockk"
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
if (!method.isInPackageName()) return
val returnType by lazy { node.returnType }
val returnTypeText by lazy { returnType?.canonicalText }
val returnTypeClass by lazy { PsiTypesUtil.getPsiClass(returnType) }
val issue = when {
// Primitives (+ String)
isPrimitive(returnTypeText.orEmpty())
|| isPrimitiveWrapper(returnType)
|| returnType?.let(::isString) == true -> ISSUE_PRIMITIVE
// Data classes
context.evaluator.isData(returnTypeClass) -> ISSUE_DATA_CLASS
// Interfaces
returnTypeClass?.isInterface == true -> ISSUE_INTERFACE
// Enums
returnTypeClass?.isEnum == true -> ISSUE_ENUM
else -> return
}
context.report(
issue = issue,
scope = node,
location = context.getLocation(node),
message = issue.getExplanation(TextFormat.TEXT),
)
}
companion object {
val ISSUE_DATA_CLASS = issue(id = "MockkDataClass", "a data class")
val ISSUE_ENUM = issue(id = "MockkEnum", "an enum")
val ISSUE_INTERFACE = issue(id = "MockkInterface", "an interface")
val ISSUE_PRIMITIVE = issue(id = "MockkPrimitive", "a primitive type")
private fun issue(id: String, what: String): Issue = Issue.create(
id = id,
briefDescription = "Detect unnecessary usages of mockk().",
explanation = "Mock of $what is unnecessary and should be replaced with a simpler instantiation.",
category = Category.TESTING,
priority = 5,
severity = Severity.WARNING,
implementation = Implementation(MockkUnnecessaryUsageDetector::class.java, EnumSet.of(JAVA_FILE, TEST_SOURCES)),
)
}
}