[jvm-inspections] IDEA-310343 Patterns as arguments for logging. Review

GitOrigin-RevId: 12fc24c595d9b2875beaa4db3ba27dd40a82ba8e
This commit is contained in:
Mikhail Pyltsin
2023-01-31 11:07:55 +00:00
committed by intellij-monorepo-bot
parent 411799afbd
commit f3a1001a0b
5 changed files with 281 additions and 234 deletions
@@ -15,7 +15,7 @@ It is recommended to use a parameterized log message instead, which will not be
logger.info("variable1: {}", variable1)
</code></pre>
<p>
Note that, the suggested replacement might not be equivalent to the original code, for example,
Note that the suggested replacement might not be equivalent to the original code, for example,
when string templates contain method calls or assignment expressions.
</p>
<!-- tooltip end -->
@@ -24,7 +24,7 @@ It is recommended to use a parameterized log message instead, which will not be
Use the <b>Warn on</b> list to ignore certain higher logging levels. Higher logging levels may be always enabled, and the arguments will always be evaluated.
</li>
<li>
Use the <b>Do not warn when only expressions with primitive types, their wrappers or String are included</b> options to ignore
Use the <b>Do not warn when only expressions with primitive types, their wrappers or String are included</b> option to ignore
string templates, which contain only expressions with primitive types, their wrappers or String.
For example, it could be useful to prevent loading lazy collections.
Note that, creating string even only with expressions with primitive types,
@@ -3,6 +3,12 @@ package com.intellij.codeInspection.logging
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.codeInspection.*
import com.intellij.codeInspection.logging.LoggingUtil.Companion
import com.intellij.codeInspection.logging.LoggingUtil.Companion.LOG_MATCHERS
import com.intellij.codeInspection.logging.LoggingUtil.Companion.countPlaceHolders
import com.intellij.codeInspection.logging.LoggingUtil.Companion.getLoggerLevel
import com.intellij.codeInspection.logging.LoggingUtil.Companion.getLoggerType
import com.intellij.codeInspection.logging.LoggingUtil.Companion.isGuarded
import com.intellij.codeInspection.options.OptPane
import com.intellij.lang.Language
import com.intellij.openapi.project.Project
@@ -24,6 +30,11 @@ class LoggingStringTemplateAsArgumentInspection : AbstractBaseUastLocalInspectio
@JvmField
var mySkipPrimitives: Boolean = true
enum class LimitLevelType {
ALL, WARN_AND_LOWER, INFO_AND_LOWER, DEBUG_AND_LOWER, TRACE
}
override fun getOptionsPane(): OptPane {
return OptPane.pane(
OptPane.dropdown(
@@ -117,12 +128,12 @@ class LoggingStringTemplateAsArgumentInspection : AbstractBaseUastLocalInspectio
val loggerLevel = getLoggerLevel(node)
if (loggerLevel == null) return true
val notSkip: Boolean = when (loggerLevel) {
LevelType.FATAL -> false
LevelType.ERROR -> false
LevelType.WARNING -> myLimitLevelType.ordinal == LimitLevelType.WARN_AND_LOWER.ordinal
LevelType.INFO -> myLimitLevelType.ordinal <= LimitLevelType.INFO_AND_LOWER.ordinal
LevelType.DEBUG -> myLimitLevelType.ordinal <= LimitLevelType.DEBUG_AND_LOWER.ordinal
LevelType.TRACE -> myLimitLevelType.ordinal <= LimitLevelType.TRACE.ordinal
Companion.LevelType.FATAL -> false
Companion.LevelType.ERROR -> false
Companion.LevelType.WARNING -> myLimitLevelType.ordinal == LimitLevelType.WARN_AND_LOWER.ordinal
Companion.LevelType.INFO -> myLimitLevelType.ordinal <= LimitLevelType.INFO_AND_LOWER.ordinal
Companion.LevelType.DEBUG -> myLimitLevelType.ordinal <= LimitLevelType.DEBUG_AND_LOWER.ordinal
Companion.LevelType.TRACE -> myLimitLevelType.ordinal <= LimitLevelType.TRACE.ordinal
}
return !notSkip
}
@@ -151,11 +162,31 @@ private fun PsiType?.isPrimitiveOrWrappers(): Boolean {
canBeText())
}
class ConvertToPlaceHolderQuickfix(private val indexStringExpression: Int) : LocalQuickFix {
private class ConvertToPlaceHolderQuickfix(private val indexStringExpression: Int) : LocalQuickFix {
override fun getFamilyName(): String = JvmAnalysisBundle.message("jvm.inspection.logging.string.template.as.argument.quickfix.name")
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val uCallExpression = descriptor.psiElement.getUastParentOfType<UCallExpression>() ?: return
val (parametersBeforeString: MutableList<UExpression>, parametersAfterString: MutableList<UExpression>, builderString) =
createMethodContext(uCallExpression)
val elementFactory = uCallExpression.getUastElementFactory(project) ?: return
val newText = elementFactory.createStringLiteralExpression(builderString.toString(), uCallExpression.sourcePsi) ?: return
val newParameters = mutableListOf<UExpression>().apply {
addAll(parametersBeforeString)
add(newText)
addAll(parametersAfterString)
}
val methodName = uCallExpression.methodName ?: return
val newCall = elementFactory.createCallExpression(uCallExpression.receiver, methodName, newParameters, uCallExpression.returnType,
uCallExpression.kind, uCallExpression.sourcePsi
) ?: return
val oldCall = uCallExpression.getQualifiedParentOrThis()
oldCall.replace(newCall)
}
private fun createMethodContext(uCallExpression: UCallExpression): MethodContext {
val parametersBeforeString: MutableList<UExpression> = mutableListOf()
val parametersAfterString: MutableList<UExpression> = mutableListOf()
val valueArguments = uCallExpression.valueArguments
@@ -185,7 +216,8 @@ class ConvertToPlaceHolderQuickfix(private val indexStringExpression: Int) : Loc
builderString.append(text)
}
else {
if (builderString.endsWith("\\") && (loggerType == LoggerType.SLF4J_LOGGER_TYPE || loggerType == LoggerType.SLF4J_BUILDER_TYPE)) {
if (builderString.endsWith("\\") &&
(loggerType == Companion.LoggerType.SLF4J_LOGGER_TYPE || loggerType == Companion.LoggerType.SLF4J_BUILDER_TYPE)) {
builderString.append("\\")
}
builderString.append("{}")
@@ -203,23 +235,10 @@ class ConvertToPlaceHolderQuickfix(private val indexStringExpression: Int) : Loc
parametersAfterString.add(valueArguments[index])
}
}
val elementFactory = uCallExpression.getUastElementFactory(project) ?: return
val newText = elementFactory.createStringLiteralExpression(builderString.toString(), uCallExpression.sourcePsi) ?: return
val newParameters = mutableListOf<UExpression>().apply {
addAll(parametersBeforeString)
add(newText)
addAll(parametersAfterString)
}
val methodName = uCallExpression.methodName ?: return
val newCall = elementFactory.createCallExpression(uCallExpression.receiver, methodName, newParameters, uCallExpression.returnType,
uCallExpression.kind, uCallExpression.sourcePsi
) ?: return
val oldCall = uCallExpression.getQualifiedParentOrThis()
oldCall.replace(newCall)
return MethodContext(parametersBeforeString, parametersAfterString, builderString)
}
}
enum class LimitLevelType {
ALL, WARN_AND_LOWER, INFO_AND_LOWER, DEBUG_AND_LOWER, TRACE
data class MethodContext(val parametersBeforeString: MutableList<UExpression>,
val parametersAfterString: MutableList<UExpression>,
val builderString: StringBuilder)
}
@@ -4,182 +4,186 @@ package com.intellij.codeInspection.logging
import com.siyeh.ig.callMatcher.CallMatcher
import org.jetbrains.uast.*
const val SLF4J_LOGGER = "org.slf4j.Logger"
internal class LoggingUtil {
companion object {
private const val SLF4J_LOGGER = "org.slf4j.Logger"
const val LOG4J_LOGGER = "org.apache.logging.log4j.Logger"
private const val LOG4J_LOGGER = "org.apache.logging.log4j.Logger"
const val LOG4J_LOG_BUILDER = "org.apache.logging.log4j.LogBuilder"
private const val LOG4J_LOG_BUILDER = "org.apache.logging.log4j.LogBuilder"
const val SLF4J_EVENT_BUILDER = "org.slf4j.spi.LoggingEventBuilder"
private const val SLF4J_EVENT_BUILDER = "org.slf4j.spi.LoggingEventBuilder"
val SLF4J_MATCHER: CallMatcher.Simple = CallMatcher.instanceCall(SLF4J_LOGGER, "trace", "debug", "info", "warn", "error")
val LOG4J_MATCHER: CallMatcher.Simple = CallMatcher.instanceCall(LOG4J_LOGGER, "trace", "debug", "info", "warn",
"error", "fatal", "log")
val LOG4J_BUILDER_MATCHER: CallMatcher.Simple = CallMatcher.instanceCall(LOG4J_LOG_BUILDER, "log")
val SLF4J_BUILDER_MATCHER: CallMatcher.Simple = CallMatcher.instanceCall(SLF4J_EVENT_BUILDER, "log")
val LOG_MATCHERS: CallMatcher = CallMatcher.anyOf(
SLF4J_MATCHER,
LOG4J_MATCHER,
LOG4J_BUILDER_MATCHER,
SLF4J_BUILDER_MATCHER,
)
private val SLF4J_MATCHER: CallMatcher.Simple = CallMatcher.instanceCall(SLF4J_LOGGER, "trace", "debug", "info", "warn", "error")
private val LOG4J_MATCHER: CallMatcher.Simple = CallMatcher.instanceCall(LOG4J_LOGGER, "trace", "debug", "info", "warn",
"error", "fatal", "log")
private val LOG4J_BUILDER_MATCHER: CallMatcher.Simple = CallMatcher.instanceCall(LOG4J_LOG_BUILDER, "log")
private val SLF4J_BUILDER_MATCHER: CallMatcher.Simple = CallMatcher.instanceCall(SLF4J_EVENT_BUILDER, "log")
val LOG_MATCHERS: CallMatcher = CallMatcher.anyOf(
SLF4J_MATCHER,
LOG4J_MATCHER,
LOG4J_BUILDER_MATCHER,
SLF4J_BUILDER_MATCHER,
)
fun getLoggerType(uCall: UCallExpression?): LoggerType? {
return if (SLF4J_MATCHER.uCallMatches(uCall)) {
LoggerType.SLF4J_LOGGER_TYPE
}
else if (LOG4J_MATCHER.uCallMatches(uCall)) {
LoggerType.LOG4J_LOGGER_TYPE
}
else if (LOG4J_BUILDER_MATCHER.uCallMatches(uCall)) {
LoggerType.LOG4J_BUILDER_TYPE
}
else if (SLF4J_BUILDER_MATCHER.uCallMatches(uCall)) {
LoggerType.SLF4J_BUILDER_TYPE
}
else {
null
}
}
fun isGuarded(call: UCallExpression): Boolean {
val variable: UVariable = getLoggerQualifier(call) ?: return false
val loggerLevel = getLoggerLevel(call) ?: return false
val ifExpression: UIfExpression? = call.getParentOfType<UIfExpression>()
val condition = ifExpression?.condition ?: return false
return isGuardedIn(condition, variable, loggerLevel)
}
fun isGuardedIn(condition: UExpression, variable: UVariable, loggerLevel: LevelType): Boolean {
val loggerLevelFromCondition: LevelType = getLevelFromCondition(condition, variable) ?: return false
return loggerLevelFromCondition == loggerLevel
}
fun getLevelFromCondition(condition: UExpression, variable: UVariable): LevelType? {
if (condition is UCallExpression) {
if ((condition.receiver as? UResolvable)?.resolveToUElement()?.sourcePsi != variable.sourcePsi) {
return null
}
val methodName = condition.methodName ?: return null
return levelTypeFromGuard(methodName)
}
if (condition is UQualifiedReferenceExpression) {
if ((condition.receiver as? UResolvable)?.resolveToUElement()?.sourcePsi != variable.sourcePsi) {
return null
}
val methodName = condition.resolvedName ?: return null
return levelTypeFromGuard(methodName)
}
if (condition is UPolyadicExpression) {
for (operand in condition.operands) {
val levelFromCondition = getLevelFromCondition(operand, variable)
if (levelFromCondition != null) return levelFromCondition
}
}
return null
}
private fun levelTypeFromGuard(methodName: String): LevelType? {
if (!methodName.startsWith("is") || !methodName.endsWith("Enabled")) {
return null
}
for (level in LevelType.values()) {
if (methodName.substring(2, methodName.length - 7).equals(level.name, ignoreCase = true)) {
return level
}
}
return null
}
fun getLoggerQualifier(call: UCallExpression?): UVariable? {
if (call == null) return null
var receiver: UExpression? = call.receiver
if (receiver is UCallExpression) {
receiver = receiver.receiver
}
if (receiver is UQualifiedReferenceExpression) {
receiver = receiver.receiver
}
if (receiver is USimpleNameReferenceExpression) {
val resolved = receiver.resolveToUElement() as? UVariable ?: return null
if (resolved.type.equalsToText(SLF4J_LOGGER) ||
resolved.type.equalsToText(LOG4J_LOGGER)) {
return resolved
}
if (resolved.type.equalsToText(SLF4J_EVENT_BUILDER) ||
resolved.type.equalsToText(LOG4J_LOG_BUILDER)) {
val uastInitializer = (resolved.uastInitializer as? UQualifiedReferenceExpression) ?: return null
return getLoggerQualifier(uastInitializer.selector as? UCallExpression)
}
}
return null
}
fun getLoggerLevel(uCall: UCallExpression?): LevelType? {
if (uCall == null) {
return null
}
var levelName = uCall.methodName
if ("log" == levelName) {
//also, it could be LOG4J_LOGGER, for example, log(level, pattern),
//but let's skip it, because it is usually used for dynamic choice
var receiver: UElement = uCall.receiver ?: return null
if (receiver is UQualifiedReferenceExpression) {
receiver = receiver.selector
}
else if (receiver is USimpleNameReferenceExpression) {
val variable = receiver.resolveToUElement() as? UVariable ?: return null
receiver = (variable.uastInitializer as? UQualifiedReferenceExpression)?.selector ?: return null
}
levelName = (receiver as? UCallExpression)?.methodName
}
if (levelName == null) {
return null
}
for (value in LevelType.values()) {
if (value.name.equals(levelName, ignoreCase = true) ||
"at${value.name}".equals(levelName, ignoreCase = true)
) {
return value
}
}
return null
}
fun countPlaceHolders(text: String, loggerType: LoggerType?): Int {
var count = 0
var placeHolder = false
var escaped = false
for (c in text) {
if (c == '\\' && (loggerType == LoggerType.SLF4J_LOGGER_TYPE || loggerType == LoggerType.SLF4J_BUILDER_TYPE)) {
escaped = !escaped
}
else if (c == '{') {
if (!escaped) {
placeHolder = true
fun getLoggerType(uCall: UCallExpression?): LoggerType? {
return if (SLF4J_MATCHER.uCallMatches(uCall)) {
LoggerType.SLF4J_LOGGER_TYPE
}
else if (LOG4J_MATCHER.uCallMatches(uCall)) {
LoggerType.LOG4J_LOGGER_TYPE
}
else if (LOG4J_BUILDER_MATCHER.uCallMatches(uCall)) {
LoggerType.LOG4J_BUILDER_TYPE
}
else if (SLF4J_BUILDER_MATCHER.uCallMatches(uCall)) {
LoggerType.SLF4J_BUILDER_TYPE
}
else {
null
}
}
else if (c == '}') {
if (placeHolder) {
count++
}
placeHolder = false
escaped = false
fun isGuarded(call: UCallExpression): Boolean {
val variable: UVariable = getLoggerQualifier(call) ?: return false
val loggerLevel = getLoggerLevel(call) ?: return false
val ifExpression: UIfExpression? = call.getParentOfType<UIfExpression>()
val condition = ifExpression?.condition ?: return false
return isGuardedIn(condition, variable, loggerLevel)
}
else {
placeHolder = false
escaped = false
private fun isGuardedIn(condition: UExpression, variable: UVariable, loggerLevel: LevelType): Boolean {
val loggerLevelFromCondition: LevelType = getLevelFromCondition(condition, variable) ?: return false
return loggerLevelFromCondition == loggerLevel
}
private fun getLevelFromCondition(condition: UExpression, variable: UVariable): LevelType? {
if (condition is UCallExpression) {
if ((condition.receiver as? UResolvable)?.resolveToUElement()?.sourcePsi != variable.sourcePsi) {
return null
}
val methodName = condition.methodName ?: return null
return levelTypeFromGuard(methodName)
}
if (condition is UQualifiedReferenceExpression) {
if ((condition.receiver as? UResolvable)?.resolveToUElement()?.sourcePsi != variable.sourcePsi) {
return null
}
val methodName = condition.resolvedName ?: return null
return levelTypeFromGuard(methodName)
}
if (condition is UPolyadicExpression) {
for (operand in condition.operands) {
val levelFromCondition = getLevelFromCondition(operand, variable)
if (levelFromCondition != null) return levelFromCondition
}
}
return null
}
private fun levelTypeFromGuard(methodName: String): LevelType? {
if (!methodName.startsWith("is") || !methodName.endsWith("Enabled")) {
return null
}
for (level in LevelType.values()) {
if (methodName.substring(2, methodName.length - 7).equals(level.name, ignoreCase = true)) {
return level
}
}
return null
}
private fun getLoggerQualifier(call: UCallExpression?): UVariable? {
if (call == null) return null
var receiver: UExpression? = call.receiver
if (receiver is UCallExpression) {
receiver = receiver.receiver
}
if (receiver is UQualifiedReferenceExpression) {
receiver = receiver.receiver
}
if (receiver is USimpleNameReferenceExpression) {
val resolved = receiver.resolveToUElement() as? UVariable ?: return null
if (resolved.type.equalsToText(SLF4J_LOGGER) ||
resolved.type.equalsToText(LOG4J_LOGGER)) {
return resolved
}
if (resolved.type.equalsToText(SLF4J_EVENT_BUILDER) ||
resolved.type.equalsToText(LOG4J_LOG_BUILDER)) {
val uastInitializer = (resolved.uastInitializer as? UQualifiedReferenceExpression) ?: return null
return getLoggerQualifier(uastInitializer.selector as? UCallExpression)
}
}
return null
}
fun getLoggerLevel(uCall: UCallExpression?): LevelType? {
if (uCall == null) {
return null
}
var levelName = uCall.methodName
if ("log" == levelName) {
//also, it could be LOG4J_LOGGER, for example, log(level, pattern),
//but let's skip it, because it is usually used for dynamic choice
var receiver: UElement = uCall.receiver ?: return null
if (receiver is UQualifiedReferenceExpression) {
receiver = receiver.selector
}
else if (receiver is USimpleNameReferenceExpression) {
val variable = receiver.resolveToUElement() as? UVariable ?: return null
receiver = (variable.uastInitializer as? UQualifiedReferenceExpression)?.selector ?: return null
}
levelName = (receiver as? UCallExpression)?.methodName
}
if (levelName == null) {
return null
}
for (value in LevelType.values()) {
if (value.name.equals(levelName, ignoreCase = true) ||
"at${value.name}".equals(levelName, ignoreCase = true)
) {
return value
}
}
return null
}
fun countPlaceHolders(text: String, loggerType: LoggerType?): Int {
var count = 0
var placeHolder = false
var escaped = false
for (c in text) {
if (c == '\\' && (loggerType == LoggerType.SLF4J_LOGGER_TYPE || loggerType == LoggerType.SLF4J_BUILDER_TYPE)) {
escaped = !escaped
}
else if (c == '{') {
if (!escaped) {
placeHolder = true
}
}
else if (c == '}') {
if (placeHolder) {
count++
}
placeHolder = false
escaped = false
}
else {
placeHolder = false
escaped = false
}
}
return count
}
enum class LoggerType {
SLF4J_LOGGER_TYPE, SLF4J_BUILDER_TYPE, LOG4J_LOGGER_TYPE, LOG4J_BUILDER_TYPE
}
enum class LevelType {
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
}
}
return count
}
enum class LoggerType {
SLF4J_LOGGER_TYPE, SLF4J_BUILDER_TYPE, LOG4J_LOGGER_TYPE, LOG4J_BUILDER_TYPE
}
enum class LevelType {
FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
}
@@ -1,36 +1,82 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.codeInspection.logging.LoggingStringTemplateAsArgumentInspection
import com.intellij.codeInspection.tests.logging.LoggingStringTemplateAsArgumentInspectionTestBase
import com.intellij.jvm.analysis.KotlinJvmAnalysisTestUtil
import com.intellij.testFramework.TestDataPath
import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
private const val INSPECTION_PATH = "/codeInspection/logging/stringTemplateAsArgument"
@TestDataPath("\$CONTENT_ROOT/testData$INSPECTION_PATH")
class KotlinLoggingStringTemplateAsArgumentInspectionTest : LoggingStringTemplateAsArgumentInspectionTestBase() {
@RunWith(Enclosed::class)
class KotlinLoggingStringTemplateAsArgumentInspectionTest {
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + INSPECTION_PATH
fun `test highlighting, myLimitLevelType=2 mySkipPrimitives=false`() {
myFixture.testHighlighting("StringTemplateAsArgumentWarnInfo.kt")
@TestDataPath("\$CONTENT_ROOT/testData$INSPECTION_PATH")
abstract class KotlinLoggingStringTemplateAsArgumentInspectionTestBase : LoggingStringTemplateAsArgumentInspectionTestBase() {
override val inspection: InspectionProfileEntry
get() = LoggingStringTemplateAsArgumentInspection()
override fun getBasePath() = KotlinJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH + INSPECTION_PATH
}
class TestCaseByDefault : KotlinLoggingStringTemplateAsArgumentInspectionTestBase() {
fun `test highlighting, with guards`() {
myFixture.testHighlighting("StringTemplateAsArgumentGuarded.kt")
}
}
fun `test highlighting, myLimitLevelType=3 mySkipPrimitives=false`() {
myFixture.testHighlighting("StringTemplateAsArgumentWarnDebug.kt")
class TestCaseMyLimitLevelTypeInfoMySkipPrimitivesFalse : KotlinLoggingStringTemplateAsArgumentInspectionTestBase() {
override val inspection: InspectionProfileEntry
get() = LoggingStringTemplateAsArgumentInspection().apply {
mySkipPrimitives = false
myLimitLevelType = LoggingStringTemplateAsArgumentInspection.LimitLevelType.INFO_AND_LOWER
}
fun `test highlighting`() {
myFixture.testHighlighting("StringTemplateAsArgumentWarnInfo.kt")
}
}
fun `test highlighting, myLimitLevelType=0 mySkipPrimitives=true`() {
myFixture.testHighlighting("StringTemplateAsArgumentSkipPrimitives.kt")
class TestCaseMyLimitLevelTypeDebugMySkipPrimitivesFalse : KotlinLoggingStringTemplateAsArgumentInspectionTestBase() {
override val inspection: InspectionProfileEntry
get() = LoggingStringTemplateAsArgumentInspection().apply {
mySkipPrimitives = false
myLimitLevelType = LoggingStringTemplateAsArgumentInspection.LimitLevelType.DEBUG_AND_LOWER
}
fun `test highlighting`() {
myFixture.testHighlighting("StringTemplateAsArgumentWarnDebug.kt")
}
}
fun `test highlighting, myLimitLevelType=0 mySkipPrimitives=false`() {
myFixture.testHighlighting("StringTemplateAsArgument.kt")
class TestCaseMyLimitLevelTypeAllMySkipPrimitivesTrue : KotlinLoggingStringTemplateAsArgumentInspectionTestBase() {
override val inspection: InspectionProfileEntry
get() = LoggingStringTemplateAsArgumentInspection().apply {
mySkipPrimitives = true
myLimitLevelType = LoggingStringTemplateAsArgumentInspection.LimitLevelType.ALL
}
fun `test highlighting`() {
myFixture.testHighlighting("StringTemplateAsArgumentSkipPrimitives.kt")
}
}
fun `test fix, myLimitLevelType=0 mySkipPrimitives=false`() {
myFixture.testQuickFix(file = "StringTemplateAsArgumentFix.kt", checkPreview = true)
}
class TestCaseMyLimitLevelTypeAllMySkipPrimitivesFalse : KotlinLoggingStringTemplateAsArgumentInspectionTestBase() {
override val inspection: InspectionProfileEntry
get() = LoggingStringTemplateAsArgumentInspection().apply {
mySkipPrimitives = false
myLimitLevelType = LoggingStringTemplateAsArgumentInspection.LimitLevelType.ALL
}
fun `test highlighting, with guards`() {
myFixture.testHighlighting("StringTemplateAsArgumentGuarded.kt")
fun `test highlighting`() {
myFixture.testHighlighting("StringTemplateAsArgument.kt")
}
fun `test fix`() {
myFixture.testQuickFix(file = "StringTemplateAsArgumentFix.kt", checkPreview = true)
}
}
}
@@ -1,25 +1,3 @@
package com.intellij.codeInspection.tests.logging
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.codeInspection.logging.LimitLevelType
import com.intellij.codeInspection.logging.LoggingStringTemplateAsArgumentInspection
abstract class LoggingStringTemplateAsArgumentInspectionTestBase : LoggingInspectionTestBase() {
override val inspection: InspectionProfileEntry
get() {
val testName = getTestName(false)
val properties = testName.split(" ").filter {
it.contains("=")
}.map { it.split("=") }
val loggingStringTemplateAsArgumentInspection = LoggingStringTemplateAsArgumentInspection()
for (property in properties) {
if (property[0] == "myLimitLevelType") {
loggingStringTemplateAsArgumentInspection.myLimitLevelType = LimitLevelType.values()[property[1].toInt()]
}
if (property[0] == "mySkipPrimitives") {
loggingStringTemplateAsArgumentInspection.mySkipPrimitives = property[1] == "true"
}
}
return loggingStringTemplateAsArgumentInspection
}
}
abstract class LoggingStringTemplateAsArgumentInspectionTestBase : LoggingInspectionTestBase()