mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[jvm-inspections] IDEA-310343 Patterns as arguments for logging. Review
GitOrigin-RevId: 12fc24c595d9b2875beaa4db3ba27dd40a82ba8e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
411799afbd
commit
f3a1001a0b
+2
-2
@@ -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,
|
||||
|
||||
+44
-25
@@ -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
|
||||
}
|
||||
+62
-16
@@ -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
-23
@@ -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()
|
||||
Reference in New Issue
Block a user