IJ-CR-105930 [java-inspections] IDEA-316635 Convert PlaceholderCountMatchesArgumentCountInspection to UAST. Fix pr

GitOrigin-RevId: 50d263a6c47eb306318ac811ff2d40762d2bb275
This commit is contained in:
Mikhail Pyltsin
2023-04-13 13:47:11 +02:00
committed by intellij-monorepo-bot
parent 518a793480
commit 557ccfa965
4 changed files with 60 additions and 125 deletions

View File

@@ -10,12 +10,14 @@ import com.intellij.codeInspection.util.InspectionMessage
import com.intellij.psi.*
import com.intellij.psi.util.InheritanceUtil
import com.intellij.uast.UastHintedVisitorAdapter
import com.intellij.util.containers.addIfNotNull
import com.siyeh.ig.callMatcher.CallMapper
import com.siyeh.ig.callMatcher.CallMatcher
import com.siyeh.ig.format.FormatDecode
import com.siyeh.ig.psiutils.TypeUtils
import org.jetbrains.uast.*
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
import org.jetbrains.uast.visitor.AbstractUastVisitor
class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLocalInspectionTool() {
@@ -73,24 +75,15 @@ class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLo
val arguments = node.valueArguments
var argumentCount = arguments.size - index
var lastArgumentIsException = hasThrowableType(arguments[arguments.size - 1])
var lastArgumentIsSupplier = couldBeThrowableSupplier(loggerType, parameters[parameters.size - 1], arguments[arguments.size - 1])
val lastArgumentIsException = hasThrowableType(arguments[arguments.size - 1])
val lastArgumentIsSupplier = couldBeThrowableSupplier(loggerType, parameters[parameters.size - 1], arguments[arguments.size - 1])
if (argumentCount == 1 && parameters.size > 1) {
val lastParameter = parameters.last()
val argument = arguments[index]
val argumentType = argument.getExpressionType()
if (argumentType is PsiArrayType && lastParameter.type is PsiArrayType) {
if (isArrayCreation(argument)) {
val initializers = getInitializers(argument) ?: return true
argumentCount = initializers.size
lastArgumentIsException = argumentCount > 0 && hasThrowableType(initializers[initializers.size - 1])
lastArgumentIsSupplier = argumentCount > 0 && couldBeThrowableSupplier(loggerType, parameters[parameters.size - 1],
initializers[initializers.size - 1])
}
else {
return true
}
return true
}
}
val logStringArgument = arguments[index - 1]
@@ -252,41 +245,6 @@ class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLo
return PlaceholderCountResult(count, if (full) PlaceholdersStatus.EXACTLY else PlaceholdersStatus.PARTIAL)
}
private fun getInitializers(argument: UExpression): List<UExpression>? {
//mostly for java
if (argument is UCallExpression && argument.kind == UastCallKind.NEW_ARRAY_WITH_INITIALIZER) {
return argument.valueArguments
}
//mostly for scala
if (argument is UBinaryExpressionWithType) {
return (argument.operand as? UCallExpression)?.valueArguments
}
//mostly for kotlin
if (argument is UCallExpression) {
return argument.valueArguments
}
//for others, don't check further
return null
}
private fun isArrayCreation(argument: UExpression): Boolean {
val argumentType = argument.getExpressionType() as? PsiArrayType ?: return false
//mostly for java
if (argumentType.equalsToText(
"java.lang.Object[]") && argument is UCallExpression && argument.kind == UastCallKind.NEW_ARRAY_WITH_INITIALIZER) return true
//mostly for kotlin
if (argument is UCallExpression) {
if (argument.receiver == null && argument.resolve() == null && argument.methodName == "arrayOf") return true
}
//mostly for scala
if (argument is UBinaryExpressionWithType && argument.operationKind == UastBinaryExpressionWithTypeKind.TypeCast.INSTANCE) {
val operand = argument.operand
if (operand is UCallExpression && operand.methodName == "Array" && operand.resolve()?.containingClass?.name == "Array$") return true
}
//for others, don't check further
return false
}
private fun couldBeThrowableSupplier(loggerType: LoggerType, lastParameter: UParameter?, lastArgument: UExpression?): Boolean {
if (loggerType != LoggerType.LOG4J_OLD_STYLE && loggerType != LoggerType.LOG4J_FORMATTED_STYLE) {
return false
@@ -303,25 +261,21 @@ class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLo
lastParameterType, "org.apache.logging.log4j.util.Supplier"))) {
return false
}
val sourcePsi = lastArgument.sourcePsi ?: return false
val sourcePsi = lastArgument.sourcePsi ?: return true
val throwable = PsiType.getJavaLangThrowable(sourcePsi.manager, sourcePsi.resolveScope)
//java lambda
if (sourcePsi is PsiLambdaExpression) {
for (expression in LambdaUtil.getReturnExpressions(sourcePsi)) {
val expressionType = expression.type
if (expression == null || expressionType == null || !throwable.isConvertibleFrom(expressionType)) {
return false
}
if (lastArgument is ULambdaExpression) {
return !lastArgument.getReturnExpressions().any {
val expressionType = it.getExpressionType()
expressionType != null && !throwable.isConvertibleFrom(expressionType)
}
return true
}
//java reference expression
if (sourcePsi is PsiMethodReferenceExpression) {
val psiType = PsiMethodReferenceUtil.getMethodReferenceReturnType(sourcePsi) ?: return false
if (lastArgument is UCallableReferenceExpression) {
val psiType = lastArgument.getMethodReferenceReturnType() ?: return true
return throwable.isConvertibleFrom(psiType)
}
//for other languages with functional interface,
//if the type is not defined, then it can be exception supplier
val type = lastArgument.getExpressionType() ?: return true
val functionalReturnType = LambdaUtil.getFunctionalInterfaceReturnType(type) ?: return true
return throwable.isConvertibleFrom(functionalReturnType)
@@ -330,12 +284,7 @@ class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLo
private fun hasThrowableType(lastArgument: UExpression): Boolean {
val type = lastArgument.getExpressionType()
if (type is PsiDisjunctionType) {
for (disjunction in type.disjunctions) {
if (!InheritanceUtil.isInheritor(disjunction, CommonClassNames.JAVA_LANG_THROWABLE)) {
return false
}
}
return true
return type.disjunctions.all { InheritanceUtil.isInheritor(it, CommonClassNames.JAVA_LANG_THROWABLE) }
}
return InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_THROWABLE)
}
@@ -476,4 +425,26 @@ class LoggingPlaceholderCountMatchesArgumentCountInspection : AbstractBaseUastLo
private data class Result(val argumentCount: Int, val placeholderCount: Int, val result: ResultType)
}
}
private fun UCallableReferenceExpression.getMethodReferenceReturnType(): PsiType? {
val method = this.resolveToUElement() as? UMethod ?: return null
if (method.isConstructor) {
val psiMethod = method.javaPsi
val containingClass = psiMethod.containingClass ?: return null
return JavaPsiFacade.getElementFactory(containingClass.project).createType(containingClass)
}
return method.returnType
}
private fun ULambdaExpression.getReturnExpressions(): List<UExpression> {
val returnExpressions = mutableListOf<UExpression>()
val visitor: AbstractUastVisitor = object : AbstractUastVisitor() {
override fun visitReturnExpression(node: UReturnExpression): Boolean {
returnExpressions.addIfNotNull(node.returnExpression)
return true
}
}
body.accept(visitor)
return returnExpressions
}

View File

@@ -3,7 +3,6 @@ package com.intellij.codeInspection.tests.java.logging
import com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspection
import com.intellij.codeInspection.tests.JvmLanguage
import com.intellij.codeInspection.tests.logging.LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
class JavaLoggingPlaceholderCountMatchesArgumentCountInspectionTest : LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase() {
@@ -276,15 +275,8 @@ class JavaLoggingPlaceholderCountMatchesArgumentCountInspectionTest : LoggingPla
""".trimIndent())
}
fun `test slf4j disable slf4jToLog4J2Type`() {
val currentProfile = ProjectInspectionProfileManager.getInstance(project).currentProfile
val inspectionTool = currentProfile.getInspectionTool(inspection.shortName, project)
val tool = inspectionTool?.tool
if (tool is LoggingPlaceholderCountMatchesArgumentCountInspection) {
tool.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
}
else {
fail()
}
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
myFixture.testHighlighting(JvmLanguage.JAVA, """
import org.slf4j.*;
class X {
@@ -325,15 +317,8 @@ class JavaLoggingPlaceholderCountMatchesArgumentCountInspectionTest : LoggingPla
""".trimIndent())
}
fun `test slf4j auto slf4jToLog4J2Type`() {
val currentProfile = ProjectInspectionProfileManager.getInstance(project).currentProfile
val inspectionTool = currentProfile.getInspectionTool(inspection.shortName, project)
val tool = inspectionTool?.tool
if (tool is LoggingPlaceholderCountMatchesArgumentCountInspection) {
tool.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.AUTO
}
else {
fail()
}
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.AUTO
myFixture.addClass("""
package org.apache.logging.slf4j;
public interface Log4jLogger {

View File

@@ -3,7 +3,6 @@ package com.intellij.codeInspection.tests.kotlin.logging
import com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspection
import com.intellij.codeInspection.tests.JvmLanguage
import com.intellij.codeInspection.tests.logging.LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
import org.junit.experimental.runners.Enclosed
import org.junit.runner.RunWith
@@ -189,7 +188,6 @@ class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionTest {
var LOG = LoggerFactory.getLogger()
fun m(a: String, b: Int, c: Any) {
LOG.info("test {} for test {} in test {}", *arrayOf(a, b, c))
LOG.info("<warning descr="More arguments provided (4) than placeholders specified (3)">test {} for test {} in test {}</warning>", *arrayOf(a, b, c, 1))
}
}
""".trimIndent())
@@ -357,6 +355,12 @@ class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionTest {
LOG.info("test {}", Supplier<Any> { "test" }, s3)
LOG.info("test {}", Supplier<Any> { "test" }, Supplier<Any> { RuntimeException() })
LOG.info("test {}", Supplier<Any> { "test" })
LOG.info("<warning descr="More arguments provided (2) than placeholders specified (1)">test {}</warning>", {""}, {" "})
LOG.info("test {}", {""}, {RuntimeException()})
val function: () -> String = { "RuntimeException()" }
LOG.info("<warning descr="More arguments provided (2) than placeholders specified (1)">test {}</warning>", {""}, function)
val function2: () -> RuntimeException = { RuntimeException() }
LOG.info("test {}", {""}, function2)
}
}
@@ -410,15 +414,7 @@ class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionTest {
class Slf4JTest : LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase() {
fun `test slf4j disable slf4jToLog4J2Type`() {
val currentProfile = ProjectInspectionProfileManager.getInstance(project).currentProfile
val inspectionTool = currentProfile.getInspectionTool(inspection.shortName, project)
val tool = inspectionTool?.tool
if (tool is LoggingPlaceholderCountMatchesArgumentCountInspection) {
tool.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
}
else {
fail()
}
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.LoggerFactory
@@ -442,15 +438,7 @@ class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionTest {
}
fun `test slf4j auto slf4jToLog4J2Type`() {
val currentProfile = ProjectInspectionProfileManager.getInstance(project).currentProfile
val inspectionTool = currentProfile.getInspectionTool(inspection.shortName, project)
val tool = inspectionTool?.tool
if (tool is LoggingPlaceholderCountMatchesArgumentCountInspection) {
tool.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.AUTO
}
else {
fail()
}
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.AUTO
myFixture.addClass("""
package org.apache.logging.slf4j;
public interface Log4jLogger {
@@ -474,15 +462,7 @@ class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionTest {
}
fun `test slf4j`() {
val currentProfile = ProjectInspectionProfileManager.getInstance(project).currentProfile
val inspectionTool = currentProfile.getInspectionTool(inspection.shortName, project)
val tool = inspectionTool?.tool
if (tool is LoggingPlaceholderCountMatchesArgumentCountInspection) {
tool.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
}
else {
fail()
}
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.NO
myFixture.testHighlighting(JvmLanguage.KOTLIN, """
import org.slf4j.*;
@@ -490,9 +470,7 @@ class KotlinLoggingPlaceholderCountMatchesArgumentCountInspectionTest {
private val brackets: String = "{}"
fun foo() {
logger?.debug("<warning descr="Fewer arguments provided (2) than placeholders specified (3)">test {} {} {}</warning>", 1, 2) //warn
logger?.debug("<warning descr="More arguments provided (3) than placeholders specified (2)">test {} {}</warning>", *arrayOf(1, 2, 3)) //warn
logger?.debug("test {} {}", *arrayOf(1, 2))
logger?.debug("<warning descr="Fewer arguments provided (2) than placeholders specified (3)">test {} {} {}</warning>", *arrayOf(1, 2, Exception())) //warn
logger?.debug("test {} {}", *arrayOf(1, 2, Exception()))
logger?.debug(<warning descr="More arguments provided (2) than placeholders specified (1)">"test " + brackets</warning>, 1, 2) //warn
logger?.debug("test {}" + brackets, 1, 2)

View File

@@ -1,13 +1,14 @@
package com.intellij.codeInspection.tests.logging
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.codeInspection.logging.LoggingPlaceholderCountMatchesArgumentCountInspection
abstract class LoggingPlaceholderCountMatchesArgumentCountInspectionTestBase : LoggingInspectionTestBase() {
override val inspection: InspectionProfileEntry
get() {
val argumentCountInspection = LoggingPlaceholderCountMatchesArgumentCountInspection()
argumentCountInspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.YES
return argumentCountInspection
}
override val inspection: LoggingPlaceholderCountMatchesArgumentCountInspection = LoggingPlaceholderCountMatchesArgumentCountInspection().apply {
this.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.YES
}
override fun tearDown() {
super.tearDown()
inspection.slf4jToLog4J2Type = LoggingPlaceholderCountMatchesArgumentCountInspection.Slf4jToLog4J2Type.YES
}
}