diff --git a/jvm/jvm-analysis-internal-testFramework/src/com/intellij/jvm/analysis/internal/testFramework/logging/LoggingArgumentSymbolReferenceProviderTestBase.kt b/jvm/jvm-analysis-internal-testFramework/src/com/intellij/jvm/analysis/internal/testFramework/logging/LoggingArgumentSymbolReferenceProviderTestBase.kt index 9d2267792e80..de17156e321b 100644 --- a/jvm/jvm-analysis-internal-testFramework/src/com/intellij/jvm/analysis/internal/testFramework/logging/LoggingArgumentSymbolReferenceProviderTestBase.kt +++ b/jvm/jvm-analysis-internal-testFramework/src/com/intellij/jvm/analysis/internal/testFramework/logging/LoggingArgumentSymbolReferenceProviderTestBase.kt @@ -1,8 +1,20 @@ package com.intellij.jvm.analysis.internal.testFramework.logging +import com.intellij.analysis.logging.resolve.LoggingArgumentSymbol import com.intellij.jvm.analysis.testFramework.LightJvmCodeInsightFixtureTestCase +import com.intellij.model.psi.PsiSymbolReference +import com.intellij.model.psi.PsiSymbolReferenceService +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiLanguageInjectionHost +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.testFramework.LightProjectDescriptor +import junit.framework.TestCase +import org.jetbrains.uast.ULiteralExpression +import org.jetbrains.uast.UPolyadicExpression abstract class LoggingArgumentSymbolReferenceProviderTestBase : LightJvmCodeInsightFixtureTestCase() { + override fun getProjectDescriptor(): LightProjectDescriptor = JAVA_LATEST_WITH_LATEST_JDK + override fun setUp() { super.setUp() LoggingTestUtils.addSlf4J(myFixture) @@ -10,4 +22,27 @@ abstract class LoggingArgumentSymbolReferenceProviderTestBase : LightJvmCodeInsi LoggingTestUtils.addJUL(myFixture) LoggingTestUtils.addKotlinAdapter(myFixture) } + + protected fun doTest(bindings: Map) { + val literalExpression = PsiTreeUtil.getParentOfType(myFixture.file.findElementAt(myFixture.editor.caretModel.offset), PsiLanguageInjectionHost::class.java) + TestCase.assertFalse(literalExpression == null) + val refs: Collection = PsiSymbolReferenceService.getService().getReferences(literalExpression!!) + TestCase.assertEquals(bindings.size, refs.size) + val usedRanges = mutableSetOf() + refs.forEach { ref -> + assertEquals(literalExpression, ref.element) + val symbols = ref.resolveReference() + assertEquals(1, symbols.size) + val symbol = symbols.single() + assertTrue(symbol is LoggingArgumentSymbol) + val formatSymbol = symbol as LoggingArgumentSymbol + val logString = formatSymbol.getPlaceholderString() + assertTrue(logString is ULiteralExpression || logString is UPolyadicExpression) + val expressionText = formatSymbol.expression.text + TestCase.assertFalse(ref.rangeInElement in usedRanges) + assertEquals(bindings[ref.rangeInElement], expressionText) + usedRanges.add(ref.rangeInElement) + } + TestCase.assertTrue(bindings.map { it.key }.toSet() == usedRanges) + } } \ No newline at end of file diff --git a/jvm/jvm-analysis-java-tests/testSrc/com/intellij/logging/resolve/JavaLoggingArgumentSymbolReferenceProviderTest.kt b/jvm/jvm-analysis-java-tests/testSrc/com/intellij/logging/resolve/JavaLoggingArgumentSymbolReferenceProviderTest.kt index ae8f7bf115b3..a4814e300aa1 100644 --- a/jvm/jvm-analysis-java-tests/testSrc/com/intellij/logging/resolve/JavaLoggingArgumentSymbolReferenceProviderTest.kt +++ b/jvm/jvm-analysis-java-tests/testSrc/com/intellij/logging/resolve/JavaLoggingArgumentSymbolReferenceProviderTest.kt @@ -1,19 +1,9 @@ package com.intellij.logging.resolve -import com.intellij.analysis.logging.resolve.LoggingArgumentSymbol import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingArgumentSymbolReferenceProviderTestBase -import com.intellij.model.psi.PsiSymbolReference -import com.intellij.model.psi.PsiSymbolReferenceService import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiLiteralExpression -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.testFramework.LightProjectDescriptor -import junit.framework.TestCase -import org.jetbrains.uast.ULiteralExpression class JavaLoggingArgumentSymbolReferenceProviderTest : LoggingArgumentSymbolReferenceProviderTestBase() { - override fun getProjectDescriptor(): LightProjectDescriptor = JAVA_LATEST_WITH_LATEST_JDK - fun `test log4j2 info`() { myFixture.configureByText("Logging.java", """ import org.apache.logging.log4j.*; @@ -358,26 +348,4 @@ class JavaLoggingArgumentSymbolReferenceProviderTest : LoggingArgumentSymbolRefe """.trimIndent()) doTest(emptyMap()) } - - private fun doTest(bindings: Map) { - val literalExpression = PsiTreeUtil.getParentOfType(myFixture.file.findElementAt(myFixture.editor.caretModel.offset), PsiLiteralExpression::class.java) - TestCase.assertFalse(literalExpression == null) - val refs: Collection = PsiSymbolReferenceService.getService().getReferences(literalExpression!!) - TestCase.assertEquals(bindings.size, refs.size) - val usedRanges = mutableSetOf() - refs.forEach { ref -> - assertEquals(literalExpression, ref.element) - val symbols = ref.resolveReference() - assertEquals(1, symbols.size) - val symbol = symbols.single() - assertTrue(symbol is LoggingArgumentSymbol) - val formatSymbol = symbol as LoggingArgumentSymbol - assertTrue(formatSymbol.getPlaceholderString() is ULiteralExpression) - val expressionText = formatSymbol.expression.text - TestCase.assertFalse(ref.rangeInElement in usedRanges) - assertEquals(bindings[ref.rangeInElement], expressionText) - usedRanges.add(ref.rangeInElement) - } - TestCase.assertTrue(bindings.map { it.key }.toSet() == usedRanges) - } } \ No newline at end of file diff --git a/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/logging/resolve/KotlinLoggingArgumentSymbolReferenceProviderTest.kt b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/logging/resolve/KotlinLoggingArgumentSymbolReferenceProviderTest.kt new file mode 100644 index 000000000000..b779cd47027e --- /dev/null +++ b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/logging/resolve/KotlinLoggingArgumentSymbolReferenceProviderTest.kt @@ -0,0 +1,351 @@ +package com.intellij.logging.resolve + +import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingArgumentSymbolReferenceProviderTestBase +import com.intellij.openapi.util.TextRange + +class KotlinLoggingArgumentSymbolReferenceProviderTest : LoggingArgumentSymbolReferenceProviderTestBase() { + fun `test log4j2 info`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + fun m(i: Int) { + LOG.info("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 debug`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + fun m(i: Int) { + LOG.debug("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 trace`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + fun m(i: Int) { + LOG.trace("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 fatal`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + fun m(i: Int) { + LOG.fatal("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 error`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + fun m(i: Int) { + LOG.fatal("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 log`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + void m(int i) { + LOG.log(Level.ALL, "{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 formatted logger`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getFormatterLogger() + fun m(int i) { + LOG.info("%d", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 default logger builder`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + void m(int i) { + LOG.atInfo().log("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 formatted logger builder`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getFormatterLogger() + void m(int i) { + LOG.atInfo().log("%d", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test log4j2 respects exception with multiple arguments`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + void m(int i) { + LOG.info("{} {}", i, Exception()) + } + } + """.trimIndent()) + + doTest(mapOf(TextRange(1, 3) to "i", TextRange(4, 6) to "Exception()")) + } + + fun `test log4j2 respects exception as single argument`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + void m(int i) { + LOG.info("{}", Exception()) + } + } + """.trimIndent()) + + doTest(emptyMap()) + } + + fun `test log4j2 respects exception in builder logger with multiple arguments`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + void m(int i) { + LOG.atInfo().log("{} {}", i, Exception()) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i", TextRange(4, 6) to "Exception()")) + } + + fun `test log4j2 respects exception in builder logger as single argument`() { + myFixture.configureByText("Logging.kt", """ + import org.apache.logging.log4j.* + class Logging { + val LOG: Logger = LogManager.getLogger() + void m(int i) { + LOG.atInfo().log("{}", Exception()) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "Exception()")) + } + + fun `test slf4j info`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.info("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test slf4j debug`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.debug("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test slf4j error`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.error("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test slf4j warn`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.warn("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + + fun `test slf4j trace`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.trace("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + + fun `test slf4j builder simple`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.atInfo().log("{}", i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "i")) + } + + fun `test slf4j builder with setMessage`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.atInfo().addArgument("foo").setMessage("{} {}").addArgument("bar").log() + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "\"foo\"", TextRange(4, 6) to "\"bar\"")) + } + + fun `test slf4j builder considers only last message`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.atInfo().addArgument("foo").setMessage("{} {} {}").setMessage("{} {}").addArgument("bar").log() + } + } + """.trimIndent()) + doTest(emptyMap()) + } + + fun `test slf4j builder combined args`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.atInfo().addArgument("foo").addArgument("bar").log("{} {} {}", "baz") + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "\"foo\"", TextRange(4, 6) to "\"bar\"", TextRange(7, 9) to "\"baz\"")) + } + + fun `test slf4j builder respects last argument as exception in log`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.atInfo().addArgument("foo").addArgument("bar").log("{} {} {}", Throwable()) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "\"foo\"", TextRange(4, 6) to "\"bar\"")) + } + + fun `test slf4j builder respects last argument as exception in setCause`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.atInfo().addArgument("foo").addArgument("bar").setCause(Throwable()).log("{} {} {}") + } + } + """.trimIndent()) + doTest(mapOf(TextRange(1, 3) to "\"foo\"", TextRange(4, 6) to "\"bar\"")) + } + + fun `test should resolve in multiline string`() { + val multilineString = "\"\"\"\n" + + "{}\n" + + "\"\"\"" + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.info($multilineString, i) + } + } + """.trimIndent()) + doTest(mapOf(TextRange(4, 6) to "i")) + } + + fun `test should not resolve with string concatenation`() { + myFixture.configureByText("Logging.kt", """ + import org.slf4j.* + class Logging { + val LOG: Logger = LoggerFactory.getLogger(Logging.class) + void m(int i) { + LOG.info("{} " + "}", i) + } + } + """.trimIndent()) + doTest(emptyMap()) + } +} \ No newline at end of file