[java-inspections] IDEA-331308 Similar logs inspection

GitOrigin-RevId: 33b43026b651dfee14eaa91e978c0c0c2b793ba2
This commit is contained in:
Mikhail Pyltsin
2024-01-05 19:37:39 +01:00
committed by intellij-monorepo-bot
parent da0308f481
commit 8efd59f2ec
8 changed files with 921 additions and 1 deletions

View File

@@ -69,6 +69,13 @@
key="jvm.inspection.logging.placeholder.count.matches.argument.count.display.name" key="jvm.inspection.logging.placeholder.count.matches.argument.count.display.name"
implementationClass="com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspection"/> implementationClass="com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspection"/>
<inspectionElementsMerger implementation="com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspectionMerger"/> <inspectionElementsMerger implementation="com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspectionMerger"/>
<localInspection language="UAST" enabledByDefault="true" level="WEAK WARNING" shortName="LoggingSimilarMessage"
groupBundle="messages.JvmAnalysisBundle" bundle="messages.JvmAnalysisBundle"
groupPathKey="jvm.inspections.group.name" groupKey="jvm.inspections.logging.frameworks.group.name"
key="jvm.inspection.logging.similar.message.display.name"
implementationClass="com.intellij.codeInspection.logging.LoggingSimilarMessageInspection"
runForWholeFile="true"
/>
<!-- Performance --> <!-- Performance -->
<localInspection language="UAST" enabledByDefault="true" level="WARNING" shortName="UrlHashCode" <localInspection language="UAST" enabledByDefault="true" level="WARNING" shortName="UrlHashCode"

View File

@@ -0,0 +1,22 @@
<html>
<body>
Reports SLF4J, Log4j2 logging calls, such as <code>logger.info("message: {}", key)</code> with similar log messages.
These calls can be non-distinguishable from each other, and this introduces difficulties to understand where a certain log message is from.
<p><b>Example (for Java):</b></p>
<pre><code lang="java">
private static void request1(String text) {
log.info("Message: {}", text); //similar call
doSomething1();
}
private static void request2(int i) {
log.info("Message: {}", i); //similar call
doSomething2();
}
</code></pre>
<!-- tooltip end -->
<p><small>New in 2024.1</small></p>
</body>
</html>

View File

@@ -173,6 +173,9 @@ jvm.inspection.logging.placeholder.count.matches.argument.count.slf4j.throwable.
jvm.inspection.logging.placeholder.count.matches.argument.count.slf4j.throwable.option.yes=Yes jvm.inspection.logging.placeholder.count.matches.argument.count.slf4j.throwable.option.yes=Yes
jvm.inspection.logging.placeholder.count.matches.argument.count.slf4j.throwable.option.no=No jvm.inspection.logging.placeholder.count.matches.argument.count.slf4j.throwable.option.no=No
jvm.inspection.logging.similar.message.display.name=Non-distinguishable logging calls
jvm.inspection.logging.similar.message.problem.descriptor=Similar log messages
jvm.inspection.logging.condition.disagrees.with.log.statement.display.name=Log condition does not match logging call jvm.inspection.logging.condition.disagrees.with.log.statement.display.name=Log condition does not match logging call
jvm.inspection.logging.condition.disagrees.with.log.statement.problem.descriptor=Level of condition ''{0}'' does not match level of logging call ''{1}'' jvm.inspection.logging.condition.disagrees.with.log.statement.problem.descriptor=Level of condition ''{0}'' does not match level of logging call ''{1}''

View File

@@ -0,0 +1,323 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.logging
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.codeInspection.AbstractBaseUastLocalInspectionTool
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.java.JavaBundle
import com.intellij.java.library.JavaLibraryUtil
import com.intellij.modcommand.ModCommand
import com.intellij.modcommand.ModCommandQuickFix
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.SmartPointerManager
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.uast.UastHintedVisitorAdapter
import org.jetbrains.annotations.Nls
import org.jetbrains.uast.*
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
import org.jetbrains.uast.visitor.AbstractUastVisitor
private const val MIN_TEXT_LENGTH = 3
private const val MAX_PART_COUNT = 10
class LoggingSimilarMessageInspection : AbstractBaseUastLocalInspectionTool() {
//otherwise results will be inconsistent
override fun runForWholeFile(): Boolean {
return true
}
override fun buildVisitor(holder: ProblemsHolder,
isOnTheFly: Boolean,
session: LocalInspectionToolSession): PsiElementVisitor {
val project = holder.project
if (!(JavaLibraryUtil.hasLibraryClass(project, LoggingUtil.SLF4J_LOGGER) ||
JavaLibraryUtil.hasLibraryClass(project, LoggingUtil.LOG4J_LOGGER))) {
return PsiElementVisitor.EMPTY_VISITOR
}
return UastHintedVisitorAdapter.create(holder.file.language, PlaceholderCountMatchesArgumentCountVisitor(holder),
arrayOf(UFile::class.java), directOnly = true)
}
inner class PlaceholderCountMatchesArgumentCountVisitor(
private val holder: ProblemsHolder,
) : AbstractUastNonRecursiveVisitor() {
override fun visitFile(node: UFile): Boolean {
val calls = collectCalls(node).toMutableSet()
if (calls.isEmpty()) return true
val groupedCalls: List<List<UCallExpression>> = calls.groupBy { it.receiver?.tryResolve().toUElementOfType<UVariable>() }
.toMutableMap()
.values.map { group ->
group.groupBy { it.methodName }.values
}.flatten()
val groups: List<List<MessageLog>> = groupedCalls.map { group ->
group.map { MessageLog(it, collectParts(it, LOGGER_TYPE_SEARCHERS.mapFirst(it)).splitWithPlaceholders()) }
.filter { log -> log.parts?.any { it.isConstant && it.text != null } ?: false }
}
for (group in groups) {
if (group.size <= 1) continue
var currentGroups: Set<List<MessageLog>> = setOf(group)
//prefilter
if (group.size > 5) {
val minLength = group
.mapNotNull { it.parts }
.filter { it.isNotEmpty() && it[0].isConstant && it[0].text != null && it[0].text?.isNotBlank() == true }
.minOfOrNull { it[0].text?.length ?: 0 } ?: return true
if (minLength == 0) return true
currentGroups = group
.groupBy {
val parts = it.parts ?: return@groupBy ""
if (parts.isEmpty()) return@groupBy ""
val part0 = parts[0]
if (!part0.isConstant || part0.text == null || part0.text.length < minLength) return@groupBy ""
part0.text.substring(0, minLength)
}
.values
.toSet()
}
for (currentGroup in currentGroups) {
for (firstIndex in 0..currentGroup.lastIndex) {
for (secondIndex in firstIndex + 1..currentGroup.lastIndex) {
if (similar(currentGroup[firstIndex].parts, currentGroup[secondIndex].parts)) {
registerProblem(holder, currentGroup[firstIndex].call, currentGroup[secondIndex].call)
registerProblem(holder, currentGroup[secondIndex].call, currentGroup[firstIndex].call)
}
}
}
}
}
return super.visitFile(node)
}
private fun collectCalls(file: UFile): Set<UCallExpression> {
val result = mutableSetOf<UCallExpression>()
file.accept(object : AbstractUastVisitor() {
override fun visitCallExpression(node: UCallExpression): Boolean {
LOGGER_TYPE_SEARCHERS.mapFirst(node) ?: return false
result.add(node)
return true
}
})
return result
}
}
private fun collectParts(node: UCallExpression, searcher: LoggerTypeSearcher?): List<LoggingStringPartEvaluator.PartHolder>? {
if (searcher == null) return null
val arguments = node.valueArguments
val method = node.resolveToUElement() as? UMethod ?: return null
val parameters = method.uastParameters
val logStringArgument: UExpression?
if (parameters.isEmpty() || arguments.isEmpty()) {
logStringArgument = findMessageSetterStringArg(node, searcher) ?: return null
}
else {
val index = getLogStringIndex(parameters) ?: return null
logStringArgument = arguments[index - 1]
}
return LoggingStringPartEvaluator.calculateValue(logStringArgument)
}
private fun registerProblem(holder: ProblemsHolder, current: UCallExpression, other: UCallExpression) {
val anchor = current.sourcePsi ?: return
val otherElement = other.sourcePsi ?: return
val commonParent = PsiTreeUtil.findCommonParent(anchor, otherElement) ?: return
val textRange = anchor.textRange
val delta = commonParent.textRange?.startOffset ?: return
holder.registerProblem(commonParent, textRange.shiftLeft(delta),
JvmAnalysisBundle.message("jvm.inspection.logging.similar.message.problem.descriptor"),
NavigateToDuplicateFix(otherElement))
}
}
private class NavigateToDuplicateFix(call: PsiElement) : ModCommandQuickFix() {
private val myPointer = SmartPointerManager.getInstance(call.project).createSmartPsiElementPointer(call)
override fun getFamilyName(): @Nls String {
return JavaBundle.message("navigate.to.duplicate.fix")
}
override fun perform(project: Project, descriptor: ProblemDescriptor): ModCommand {
val element = myPointer.element
if (element == null) return ModCommand.nop()
return ModCommand.select(element)
}
}
private fun List<LoggingStringPartEvaluator.PartHolder>?.splitWithPlaceholders(): List<LoggingStringPartEvaluator.PartHolder>? {
if (this == null) return null
val result = mutableListOf<LoggingStringPartEvaluator.PartHolder>()
for (partHolder in this) {
if (partHolder.isConstant && partHolder.text != null) {
val withoutPlaceholders = partHolder.text.split("{}")
for ((index, clearPart) in withoutPlaceholders.withIndex()) {
result.add(LoggingStringPartEvaluator.PartHolder(clearPart, true))
if (index != withoutPlaceholders.lastIndex) {
result.add(LoggingStringPartEvaluator.PartHolder(clearPart, false))
}
}
}
else {
result.add(partHolder)
}
}
return result
}
private class PartHolderIterator(private val parts: List<LoggingStringPartEvaluator.PartHolder>) {
private var i = 0
private var partial: String? = null
fun hasNext(): Boolean {
return i < parts.size
}
fun isText(): Boolean {
val partHolder = parts[i]
return partHolder.isConstant && partHolder.text != null
}
fun move() {
i++
partial = null
}
fun current(): String? {
val local = partial
if (local != null) {
return local
}
val partHolder = parts[i]
return partHolder.text
}
fun isFirst(): Boolean {
return i == 0
}
fun move(delta: Int) {
partial = current()?.substring(delta)
}
fun isLast(): Boolean {
return i == parts.lastIndex
}
fun previousUnknown(): Boolean {
return if (i == 0) {
false
}
else {
val partHolder = parts[i - 1]
!partHolder.isConstant || partHolder.text == null
}
}
}
private data class MessageLog(val call: UCallExpression, val parts: List<LoggingStringPartEvaluator.PartHolder>?)
private fun similar(first: List<LoggingStringPartEvaluator.PartHolder>?,
second: List<LoggingStringPartEvaluator.PartHolder>?): Boolean {
if (first == null || second == null) return false
if (first.isEmpty() || second.isEmpty()) return false
if (first.size >= MAX_PART_COUNT || second.size >= MAX_PART_COUNT) return false
val firstIterator = PartHolderIterator(first)
val secondIterator = PartHolderIterator(second)
var intersection = 0
while (firstIterator.hasNext() && secondIterator.hasNext()) {
if (!firstIterator.isText()) {
firstIterator.move()
continue
}
if (!secondIterator.isText()) {
secondIterator.move()
continue
}
if (firstIterator.current() == secondIterator.current()) {
intersection += firstIterator.current()?.length ?: 0
firstIterator.move()
secondIterator.move()
continue
}
if (firstIterator.isFirst() && secondIterator.isFirst()) {
if (firstIterator.current()?.startsWith(secondIterator.current() ?: "") == true) {
val delta = secondIterator.current()?.length ?: 0
intersection += delta
secondIterator.move()
firstIterator.move(delta)
continue
}
if (secondIterator.current()?.startsWith(firstIterator.current() ?: "") == true) {
val delta = firstIterator.current()?.length ?: 0
intersection += delta
firstIterator.move()
secondIterator.move(delta)
continue
}
return false
}
if (firstIterator.isLast() && secondIterator.isLast()) {
if (firstIterator.current()?.endsWith(secondIterator.current() ?: "") == true) {
val delta = secondIterator.current()?.length ?: 0
intersection += delta
firstIterator.move()
secondIterator.move()
continue
}
if (secondIterator.current()?.endsWith(firstIterator.current() ?: "") == true) {
val delta = firstIterator.current()?.length ?: 0
intersection += delta
firstIterator.move()
secondIterator.move()
continue
}
return false
}
//There can be not only included staff, but intersection.
//This intersection is skipped deliberately because it can be used in real logs
if (secondIterator.previousUnknown()) {
var delta = firstIterator.current()?.indexOf(secondIterator.current() ?: "") ?: -1
if (delta != -1) {
delta += secondIterator.current()?.length ?: 0
intersection += delta
secondIterator.move()
firstIterator.move(delta)
continue
}
}
if (firstIterator.previousUnknown()) {
var delta = secondIterator.current()?.indexOf(firstIterator.current() ?: "") ?: -1
if (delta != -1) {
delta += firstIterator.current()?.length ?: 0
intersection += delta
firstIterator.move()
secondIterator.move(delta)
continue
}
}
return false
}
return intersection >= MIN_TEXT_LENGTH
}

View File

@@ -22,7 +22,13 @@ internal class LoggingStringPartEvaluator {
companion object { companion object {
fun calculateValue(expression: UExpression): List<PartHolder>? { fun calculateValue(expression: UExpression): List<PartHolder>? {
if (!isString(expression)) return null if (!isString(expression)) return null
return tryJoin(recursiveCalculateValue(expression, Context(depth = 10, maxParts = 20))) val sourcePsi = expression.sourcePsi ?: return null
val project = sourcePsi.project
return CachedValuesManager.getManager(project).getCachedValue(sourcePsi, CachedValueProvider {
return@CachedValueProvider CachedValueProvider.Result.create(
tryJoin(recursiveCalculateValue(sourcePsi.toUElementOfType<UExpression>(), Context(depth = 10, maxParts = 20))),
PsiModificationTracker.MODIFICATION_COUNT)
})
} }
private fun recursiveCalculateValue(expression: UExpression?, private fun recursiveCalculateValue(expression: UExpression?,

View File

@@ -0,0 +1,7 @@
package com.intellij.jvm.analysis.internal.testFramework.logging
import com.intellij.codeInspection.logging.LoggingSimilarMessageInspection
abstract class LoggingSimilarMessageInspectionTestBase : LoggingInspectionTestBase() {
override val inspection = LoggingSimilarMessageInspection()
}

View File

@@ -0,0 +1,501 @@
package com.intellij.codeInspection.tests.java.logging
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingSimilarMessageInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
class JavaLoggingSimilarMessageInspectionTest : LoggingSimilarMessageInspectionTestBase() {
fun `test equals log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test not completed log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = "{}" + i;
LOG.info(msg);
}
private static void request2(int i) {
String msg = "{}" + i;
LOG.info(msg);
}
}
""".trimIndent())
}
fun `test not completed 2 log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = "{} {}";
LOG.info(msg);
}
private static void request2(int i) {
String msg = "{} {}";
LOG.info(msg);
}
}
""".trimIndent())
}
fun `test many equals log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = "log messages2: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request3(int i) {
String msg = "log messages2: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request4(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request5(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request6(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request7(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request8(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request9(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request10(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test equals slf4j`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.slf4j.*;
class Logging {
private static Logger LOG = LoggerFactory.getLogger(Logging.class);
private static void request1(String i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test setMessage equals slf4j`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.slf4j.*;
class Logging {
private static Logger LOG = LoggerFactory.getLogger(Logging.class);
private static void request1(String i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test not equals level slf4j`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.slf4j.*;
import org.slf4j.spi.*;
class X {
void foo() {
Logger logger = LoggerFactory.getLogger(X.class);
<weak_warning descr="Similar log messages">logger.atError()
.setMessage("aaa {}")
.log()</weak_warning>;
<weak_warning descr="Similar log messages">logger.atError()
.setMessage("aaa 2{}")
.log()</weak_warning>;
}
}
""".trimIndent())
}
fun `test not equals log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = "log 2 messages: " + i;
LOG.info(msg);
}
private static void request2(int i) {
String msg = "log messages: " + i;
LOG.info(msg);
}
}
""".trimIndent())
}
fun `test not equals end log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log 2 messages";
LOG.info(msg);
}
private static void request2(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
}
""".trimIndent())
}
fun `test endWith log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + "2: log messages";
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = i + ": log messages";
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test endWith2 log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log messages";
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = i + "2: log messages";
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test startWith log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = "log messages" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = "log messages2" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test startWith 2 log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = "log messages2" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = "log messages" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test equals end log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log messages";
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = i + ": log messages";
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test many equals end log4j2 without highlighting`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request2(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request3(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request4(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request5(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request6(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request7(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request8(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request9(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
private static void request10(int i) {
String msg = i + ": log messages";
LOG.info(msg);
}
}
""".trimIndent())
}
fun `test equals middle log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = i + ": log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test equals middle contains log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log messages: 2" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = i + ": log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test equals middle contains 2 log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = i + ": log messages: 2" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test equals middle contains 3 log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + "2: log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = i + ": log messages:" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test equals middle contains 4 log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log messages:" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
private static void request2(int i) {
String msg = i + "2: log messages:" + i;
<weak_warning descr="Similar log messages">LOG.info(msg)</weak_warning>;
}
}
""".trimIndent())
}
fun `test not equals middle log4j2`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
String msg = i + ": log messages: " + i;
LOG.info(msg);
}
private static void request2(int i) {
String msg = i + ": log 2 messages: " + i;
LOG.info(msg);
}
}
""".trimIndent())
}
fun `test equals log4j2 with placeholders`() {
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.apache.logging.log4j.*;
class Logging {
private static final Logger LOG = LogManager.getLogger();
private static void request1(String i) {
<weak_warning descr="Similar log messages">LOG.info("log messages: ", i)</weak_warning>;
}
private static void request2(int i) {
String msg = "log messages: " + i;
<weak_warning descr="Similar log messages">LOG.info("log messages: ", i)</weak_warning>;
}
}
""".trimIndent())
}
}

View File

@@ -0,0 +1,51 @@
package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.jvm.analysis.internal.testFramework.logging.LoggingSimilarMessageInspectionTestBase
import com.intellij.jvm.analysis.testFramework.JvmLanguage
class KotlinLoggingSimilarMessageInspectionTest : LoggingSimilarMessageInspectionTestBase() {
fun `test equals log4j2`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
internal class Logging {
private val logger: Logger = LogManager.getLogger()
private fun request1(i: String) {
val msg = "log messages: {}"
logger.<weak_warning descr="Similar log messages">info(msg, i)</weak_warning>
}
private fun request2(i: Int) {
val msg = "log messages: {}"
logger.<weak_warning descr="Similar log messages">info(msg, i)</weak_warning>
}
}
""".trimIndent())
}
fun `test equals slf4j`() {
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.Logger
import org.slf4j.LoggerFactory
internal class Logging {
private val LOG: Logger = LoggerFactory.getLogger(Logging::class.<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: java">java</error>)
private fun request1(i: String) {
val msg = "log messages: {}"
LOG.<weak_warning descr="Similar log messages">info(msg, i)</weak_warning>
LOG.info("1" + msg, i)
}
private fun request2(i: Int) {
val msg = "log messages: {}"
LOG.<weak_warning descr="Similar log messages">info(msg, i)</weak_warning>
}
}
""".trimIndent())
}
}