[Java. Logging] Restore support for escape symbols in LoggingArgumentSymbolReferenceProvider

IDEA-342484

GitOrigin-RevId: 68d3e58e45554128c59d0a81d4d24ad415b8b812
This commit is contained in:
Georgii Ustinov
2024-04-25 14:08:33 +03:00
committed by intellij-monorepo-bot
parent 1831451cdb
commit 0e73acf094
5 changed files with 154 additions and 61 deletions

View File

@@ -2,8 +2,6 @@
package com.intellij.analysis.logging.resolve
import com.intellij.codeInspection.logging.*
import com.intellij.codeInspection.logging.PlaceholderCountIndexStrategy.KOTLIN_MULTILINE_RAW_STRING
import com.intellij.codeInspection.logging.PlaceholderCountIndexStrategy.RAW_STRING
import com.intellij.codeInspection.logging.PlaceholderLoggerType.*
import com.intellij.model.Symbol
import com.intellij.model.psi.PsiExternalReferenceHost
@@ -73,7 +71,17 @@ internal fun <T> getAlignedPlaceholderCount(placeholderList: List<T>, context: P
internal fun getPlaceholderRanges(context: PlaceholderContext): List<PlaceholderRanges>? {
val logStringText = context.logStringArgument.sourcePsi?.text ?: return null
val type = if (isKotlinMultilineString(context.logStringArgument, logStringText)) KOTLIN_MULTILINE_RAW_STRING else RAW_STRING
val type = if (isKotlinMultilineString(context.logStringArgument, logStringText)) {
KOTLIN_MULTILINE_RAW_STRING
}
else if (isKotlinString(context.logStringArgument)) {
KOTLIN_RAW_STRING
}
else if (isJavaString(context.logStringArgument)) {
JAVA_RAW_STRING
}
else return null
val partHolders = listOf(
LoggingStringPartEvaluator.PartHolder(
logStringText,
@@ -86,6 +94,14 @@ internal fun getPlaceholderRanges(context: PlaceholderContext): List<Placeholder
return placeholderCountResult.placeholderRangesList
}
private fun isKotlinMultilineString(logString: UExpression, text : String): Boolean {
return logString is UPolyadicExpression && text.startsWith("\"\"\"") && text.endsWith("\"\"\"") && text.length >= 6
private fun isKotlinString(logString: UExpression): Boolean {
return logString is UPolyadicExpression
}
private fun isJavaString(logString: UExpression): Boolean {
return logString is ULiteralExpression
}
private fun isKotlinMultilineString(logString: UExpression, text: String): Boolean {
return isKotlinString(logString) && text.startsWith("\"\"\"") && text.endsWith("\"\"\"") && text.length >= 6
}

View File

@@ -56,7 +56,7 @@ class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLo
var finalArgumentCount = context.placeholderParameters.size
val placeholderCountHolder = solvePlaceholderCount(context.loggerType, finalArgumentCount, context.partHolderList, PlaceholderCountIndexStrategy.UAST_STRING)
val placeholderCountHolder = solvePlaceholderCount(context.loggerType, finalArgumentCount, context.partHolderList, UAST_STRING)
if (placeholderCountHolder.status == PlaceholdersStatus.EMPTY) {
return true

View File

@@ -124,24 +124,35 @@ internal enum class PlaceholdersStatus {
EXACTLY, PARTIAL, ERROR_TO_PARSE_STRING, EMPTY
}
internal enum class PlaceholderCountIndexStrategy {
UAST_STRING {
override fun shiftAfterEscapeChar(index: Int): Int {
return index
}
},
KOTLIN_MULTILINE_RAW_STRING {
override fun shiftAfterEscapeChar(index: Int): Int {
return index
}
},
RAW_STRING {
override fun shiftAfterEscapeChar(index: Int): Int {
return index + 1
}
};
internal interface PlaceholderCountIndexStrategy {
fun shiftAfterEscapeChar(text: String, index: Int): Int {
assert(index in text.indices && text[index] == '\\')
return index
}
}
abstract fun shiftAfterEscapeChar(index: Int): Int
internal abstract class RawStringPlaceholderCountStrategy : PlaceholderCountIndexStrategy {
protected abstract val escapeSymbols: Set<Char>
override fun shiftAfterEscapeChar(text: String, index: Int): Int {
assert(index in text.indices && text[index] == '\\')
if (index + 1 !in text.indices || isEscapeSymbol(text[index + 1])) return index
return index + 1
}
private fun isEscapeSymbol(char: Char): Boolean = char in escapeSymbols
}
internal val UAST_STRING = object : PlaceholderCountIndexStrategy {}
internal val KOTLIN_MULTILINE_RAW_STRING = object : PlaceholderCountIndexStrategy {}
internal val KOTLIN_RAW_STRING = object : RawStringPlaceholderCountStrategy() {
override val escapeSymbols: Set<Char> = setOf('t', 'b', 'n', 'r', '$', '\'', '\"')
}
internal val JAVA_RAW_STRING = object : RawStringPlaceholderCountStrategy() {
override val escapeSymbols: Set<Char> = setOf('t', 'b', 'n', 'r', 'f', '\'', '\"')
}
@@ -468,17 +479,17 @@ private fun countBracesBasedPlaceholders(holders: List<LoggingStringPartEvaluato
full = false
continue
}
val string = partHolder.text ?: continue
val length = string.length
val text = partHolder.text ?: continue
val length = text.length
var escaped = false
var lastPlaceholderIndex = -1
var i = 0
while (i < length) {
val c = string[i]
val c = text[i]
if (c == '\\' &&
(loggerType == PlaceholderLoggerType.SLF4J_EQUAL_PLACEHOLDERS || loggerType == PlaceholderLoggerType.SLF4J)) {
escaped = !escaped
i = placeholderCountShiftIndexStrategy.shiftAfterEscapeChar(i)
i = placeholderCountShiftIndexStrategy.shiftAfterEscapeChar(text, i)
}
else if (c == '{') {
if (holderIndex != 0 && i == 0 && !holders[holderIndex - 1].isConstant) {

View File

@@ -465,63 +465,96 @@ class JavaLoggingArgumentSymbolReferenceProviderTest : LoggingArgumentSymbolRefe
doTest(emptyMap())
}
fun `test should resolve with escape character in simple string log4j2`() {
fun `test resolve with escape characters in simple string log4j2`() {
myFixture.configureByText("Logging.java", """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
void m(int i) {
LOG.info("\\{<caret>}", i);
LOG.info("\"{} \'{<caret>} \f{} \t{} \b{} \n{} \r{} \\{}", 1, 2, 3, 4, 5, 6, 7, 8);
}
}
""".trimIndent())
doTest(mapOf(TextRange(3, 5) to "i"))
doTest(mapOf(
TextRange(3, 5) to "1",
TextRange(8, 10) to "2",
TextRange(13, 15) to "3",
TextRange(18, 20) to "4",
TextRange(23, 25) to "5",
TextRange(28, 30) to "6",
TextRange(33, 35) to "7",
TextRange(38, 40) to "8",
))
}
fun `test should resolve with escape character in multiline string log4j2`() {
fun `test resolve with escape characters in multiline string log4j2`() {
val multilineString = "\"\"\"\n" +
"\\\\{<caret>}" +
"\\\"{} \\'{<caret>} \\f{} \\t{} \\b{} \\n{} \\r{} \\\\{}" +
"\"\"\""
myFixture.configureByText("Logging.java", """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
void m(int i) {
LOG.info($multilineString, i);
LOG.info($multilineString, 1, 2, 3, 4, 5, 6, 7, 8);
}
}
""".trimIndent())
doTest(mapOf(TextRange(6, 8) to "i"))
doTest(mapOf(
TextRange(6, 8) to "1",
TextRange(11, 13) to "2",
TextRange(16, 18) to "3",
TextRange(21, 23) to "4",
TextRange(26, 28) to "5",
TextRange(31, 33) to "6",
TextRange(36, 38) to "7",
TextRange(41, 43) to "8",
))
}
fun `test should not resolve with escape character in simple string slf4j`() {
fun `test resolve with escape characters in simple string slf4j`() {
myFixture.configureByText("Logging.java", """
import org.slf4j.*;
class Logging {
private static final Logger LOG = LoggerFactory.getLogger(Logging.class);
void m(int i) {
LOG.info("\\{<caret>}", i);
LOG.info("\"{} \'{<caret>} \f{} \t{} \b{} \n{} \r{} \\{}", 1, 2, 3, 4, 5, 6, 7);
}
}
""".trimIndent())
doTest(emptyMap())
doTest(mapOf(
TextRange(3, 5) to "1",
TextRange(8, 10) to "2",
TextRange(13, 15) to "3",
TextRange(18, 20) to "4",
TextRange(23, 25) to "5",
TextRange(28, 30) to "6",
TextRange(33, 35) to "7",
))
}
fun `test should not resolve with escape character in multiline string slf4j`() {
fun `test resolve with escape characters in multiline string slf4j`() {
val multilineString = "\"\"\"\n" +
"\\\\{<caret>}" +
"\\\"{} \\'{<caret>} \\f{} \\t{} \\b{} \\n{} \\r{} \\\\{}" +
"\"\"\""
myFixture.configureByText("Logging.java", """
import org.slf4j.*;
class Logging {
private static final Logger LOG = LoggerFactory.getLogger(Logging.class);
void m(int i) {
LOG.info($multilineString, i);
LOG.info($multilineString, 1, 2, 3, 4, 5, 6, 7);
}
}
""".trimIndent())
doTest(emptyMap())
doTest(mapOf(
TextRange(6, 8) to "1",
TextRange(11, 13) to "2",
TextRange(16, 18) to "3",
TextRange(21, 23) to "4",
TextRange(26, 28) to "5",
TextRange(31, 33) to "6",
TextRange(36, 38) to "7",
))
}
}

View File

@@ -483,63 +483,96 @@ class KotlinLoggingArgumentSymbolReferenceProviderTest : LoggingArgumentSymbolRe
doTest(emptyMap())
}
fun `test should resolve with escape character in simple string log4j2`() {
fun `test resolve with escape characters in simple string log4j2`() {
myFixture.configureByText("Logging.kt", """
import org.apache.logging.log4j.*
class Logging {
val LOG: Logger = LoggerFactory.getLogger(Logging.class)
val LOG: Logger = LogManager.getLogger(Logging.class)
fun m(i: Int) {
LOG.info("\\{<caret>}", i)
LOG.info("\"{} \'{<caret>} \${"$"}{} \t{} \b{} \n{} \r{} \\{}", 1, 2, 3, 4, 5, 6, 7, 8)
}
}
""".trimIndent())
doTest(mapOf(TextRange(3, 5) to "i"))
doTest(mapOf(
TextRange(3, 5) to "1",
TextRange(8, 10) to "2",
TextRange(13, 15) to "3",
TextRange(18, 20) to "4",
TextRange(23, 25) to "5",
TextRange(28, 30) to "6",
TextRange(33, 35) to "7",
TextRange(38, 40) to "8",
))
}
fun `test should resolve with escape character in multiline string log4j2`() {
fun `test resolve with escape characters in multiline string log4j2`() {
val multilineString = "\"\"\"\n" +
"\\\\{<caret>}" +
"\\\"{} \\'{<caret>} \\t{} \\b{} \\n{} \\r{} \\\\{}" +
"\"\"\""
myFixture.configureByText("Logging.kt", """
import org.apache.logging.log4j.*
class Logging {
val LOG: Logger = LoggerFactory.getLogger(Logging.class)
val LOG: Logger = LogManager.getLogger(Logging.class)
fun m(i: Int) {
LOG.info("$multilineString", i)
LOG.info("$multilineString", 1, 2, 3, 4, 5, 6, 7)
}
}
""".trimIndent())
doTest(mapOf(TextRange(7, 9) to "i"))
doTest(mapOf(
TextRange(7, 9) to "1",
TextRange(12, 14) to "2",
TextRange(17, 19) to "3",
TextRange(22, 24) to "4",
TextRange(27, 29) to "5",
TextRange(32, 34) to "6",
TextRange(37, 39) to "7",
))
}
fun `test should not resolve with escape character in simple string slf4j`() {
fun `test resolve with escape characters in simple string slf4j`() {
myFixture.configureByText("Logging.kt", """
import org.slf4j.*
class Logging {
val LOG: Logger = LogManager.getFormatterLogger()
val LOG: Logger = LoggerFactory.getLogger()
fun m(i: Int) {
LOG.info("\\{<caret>}", i)
LOG.info("\"{} \'{<caret>} \${"$"}{} \t{} \b{} \n{} \r{} \\{}", 1, 2, 3, 4, 5, 6, 7)
}
}
""".trimIndent())
doTest(emptyMap())
doTest(mapOf(
TextRange(3, 5) to "1",
TextRange(8, 10) to "2",
TextRange(13, 15) to "3",
TextRange(18, 20) to "4",
TextRange(23, 25) to "5",
TextRange(28, 30) to "6",
TextRange(33, 35) to "7",
))
}
fun `test should resolve with escape character in multiline string slf4j`() {
fun `test resolve with escape characters in multiline string slf4j`() {
val multilineString = "\"\"\"\n" +
"\\\\{<caret>}" +
"\\\"{} \\'{<caret>} \\t{} \\b{} \\n{} \\r{} \\\\{}" +
"\"\"\""
myFixture.configureByText("Logging.kt", """
import org.slf4j.*
class Logging {
val LOG: Logger = LoggerFactory.getLogger(Logging.class)
val LOG: Logger = LoggerFactory.getLogger()
fun m(i: Int) {
LOG.info("$multilineString", i)
LOG.info("$multilineString", 1, 2, 3, 4, 5, 6, 7)
}
}
""".trimIndent())
doTest(mapOf(TextRange(7, 9) to "i"))
doTest(mapOf(
TextRange(7, 9) to "1",
TextRange(12, 14) to "2",
TextRange(17, 19) to "3",
TextRange(22, 24) to "4",
TextRange(27, 29) to "5",
TextRange(32, 34) to "6",
TextRange(37, 39) to "7",
))
}
}