Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sks24git committed Nov 22, 2024
1 parent 689343a commit 7741fae
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright © 2024 Explyt Ltd
*
* All rights reserved.
*
* This code and software are the property of Explyt Ltd and are protected by copyright and other intellectual property laws.
*
* You may use this code under the terms of the Explyt Source License Version 1.0 ("License"), if you accept its terms and conditions.
*
* By installing, downloading, accessing, using, or distributing this code, you agree to the terms and conditions of the License.
* If you do not agree to such terms and conditions, you must cease using this code and immediately delete all copies of it.
*
* You may obtain a copy of the License at: https://github.com/explyt/spring-plugin/blob/main/EXPLYT-SOURCE-LICENSE.md
*
* Unauthorized use of this code constitutes a violation of intellectual property rights and may result in legal action.
*/

package com.explyt.spring.core.inspections

import com.explyt.inspection.SpringBaseUastLocalInspectionTool
import com.explyt.spring.core.SpringCoreBundle
import com.explyt.spring.core.SpringCoreClasses
import com.explyt.spring.core.inspections.quickfix.AddAsMethodArgQuickFix
import com.explyt.util.ExplytAnnotationUtil.getUMetaAnnotation
import com.explyt.util.ExplytPsiUtil.findChildrenOfType
import com.explyt.util.ExplytPsiUtil.getHighlightRange
import com.explyt.util.ExplytPsiUtil.isMetaAnnotatedBy
import com.explyt.util.ExplytPsiUtil.toSourcePsi
import com.intellij.codeInsight.AnnotationUtil
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiIdentifier
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.*

class SpringConfigurationProxyBeanMethodsInspection : SpringBaseUastLocalInspectionTool() {

override fun checkMethod(
uMethod: UMethod,
manager: InspectionManager,
isOnTheFly: Boolean
): Array<ProblemDescriptor>? {
val method = uMethod.javaPsi
val uClass = uMethod.getParentOfType<UClass>() ?: return null
val surroundingClass: PsiClass = uClass.javaPsi
if (uClass.isStatic) return null
if (!method.isMetaAnnotatedBy(SpringCoreClasses.BEAN)) return null
var topClass: PsiClass? = surroundingClass

while (topClass != null) {
val proxyBeanMethodsValue = uClass.getUMetaAnnotation(SpringCoreClasses.CONFIGURATION)
?.javaPsi?.let {
AnnotationUtil.getBooleanAttributeValue(it, "proxyBeanMethods")
}

if (proxyBeanMethodsValue == false) {
return findCallsToLocalBeans(method, topClass).asSequence()
.mapNotNull {
createProblemDescriptor(
manager,
SpringCoreBundle.message("explyt.spring.inspection.configuration.proxy.incorrect"),
it,
isOnTheFly
)
}.toList().toTypedArray()
} else if (proxyBeanMethodsValue == null //not metaAnnotated by Configuration
&& topClass.isMetaAnnotatedBy(SpringCoreClasses.COMPONENT)
) {
return findCallsToLocalBeans(method, topClass).asSequence()
.mapNotNull {
createProblemDescriptor(
manager,
SpringCoreBundle.message("explyt.spring.inspection.configuration.light-bean.incorrect"),
it,
isOnTheFly
)
}.toList().toTypedArray()
}
topClass = topClass.containingClass
}
return null
}

private fun createProblemDescriptor(
manager: InspectionManager,
message: String,
callExpression: UCallExpression,
isOnTheFly: Boolean
): ProblemDescriptor? {
val identifier = callExpression.methodIdentifier?.sourcePsi ?: return null
val fixes = listOfNotNull(
(identifier as? PsiIdentifier)
?.let { AddAsMethodArgQuickFix(it) }
).toTypedArray()

return manager.createProblemDescriptor(
identifier,
identifier.getHighlightRange(),
message,
ProblemHighlightType.GENERIC_ERROR,
isOnTheFly,
*fixes
)
}

private fun findCallsToLocalBeans(psiMethod: PsiMethod, surroundingClass: PsiClass): List<UCallExpression> {
val beanMethods = surroundingClass.methods
.filter { it.isMetaAnnotatedBy(SpringCoreClasses.BEAN) }
.toSet()

return psiMethod.toSourcePsi()?.findChildrenOfType<PsiElement>()?.asSequence()
?.mapNotNull { it.toUElement() as? UCallExpression }
?.filter { beanMethods.contains(it.resolve()) }
?.toList()
?: emptyList()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright © 2024 Explyt Ltd
*
* All rights reserved.
*
* This code and software are the property of Explyt Ltd and are protected by copyright and other intellectual property laws.
*
* You may use this code under the terms of the Explyt Source License Version 1.0 ("License"), if you accept its terms and conditions.
*
* By installing, downloading, accessing, using, or distributing this code, you agree to the terms and conditions of the License.
* If you do not agree to such terms and conditions, you must cease using this code and immediately delete all copies of it.
*
* You may obtain a copy of the License at: https://github.com/explyt/spring-plugin/blob/main/EXPLYT-SOURCE-LICENSE.md
*
* Unauthorized use of this code constitutes a violation of intellectual property rights and may result in legal action.
*/

package com.explyt.spring.core.inspections

import com.explyt.inspection.SpringBaseUastLocalInspectionTool
import com.explyt.spring.core.SpringCoreBundle
import com.explyt.spring.core.SpringCoreClasses
import com.explyt.spring.core.SpringCoreClasses.ANNOTATIONS_CACHE
import com.explyt.util.ExplytPsiUtil.isEqualOrInheritor
import com.explyt.util.ExplytPsiUtil.isMetaAnnotatedBy
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiJvmModifiersOwner
import com.intellij.psi.PsiNameIdentifierOwner
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UDeclaration
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.getContainingUClass

class SpringInterfaceCacheAnnotationsInspection : SpringBaseUastLocalInspectionTool() {

override fun checkMethod(
uMethod: UMethod,
manager: InspectionManager,
isOnTheFly: Boolean
): Array<ProblemDescriptor>? {
val uClass = uMethod.getContainingUClass() ?: return null
if (!uClass.isInterface) return null
val psiClass = uClass.javaPsi
if (isSubclassedBuSpring(psiClass)) return null

return check(
uMethod,
manager,
isOnTheFly,
SpringCoreBundle.message("explyt.spring.inspection.cache.interface.method")
)
}

override fun checkClass(
uClass: UClass,
manager: InspectionManager,
isOnTheFly: Boolean
): Array<ProblemDescriptor>? {
if (uClass.isAnnotationType || !uClass.isInterface) return null
val psiClass = uClass.javaPsi
if (isSubclassedBuSpring(psiClass)) return null

return check(
uClass,
manager,
isOnTheFly,
SpringCoreBundle.message("explyt.spring.inspection.cache.interface.class")
)
}

private fun check(
uElement: UDeclaration,
manager: InspectionManager,
isOnTheFly: Boolean,
errorMessage: String
): Array<ProblemDescriptor>? {
val psiElement = uElement.javaPsi as? PsiJvmModifiersOwner ?: return null

if (!psiElement.isMetaAnnotatedBy(ANNOTATIONS_CACHE)) return null
val identifyingElement = (uElement.sourcePsi as? PsiNameIdentifierOwner)?.identifyingElement ?: return null

return arrayOf(
manager.createProblemDescriptor(
identifyingElement,
errorMessage,
isOnTheFly,
emptyArray(),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
)
)
}

private fun isSubclassedBuSpring(psiClass: PsiClass): Boolean {

return psiClass.isEqualOrInheritor("org.springframework.data.repository.Repository")
|| psiClass.isMetaAnnotatedBy(
listOf(
SpringCoreClasses.NETFLIX_FEIGN_CLIENT,
SpringCoreClasses.OPEN_FEIGN_CLIENT
)
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<html>
<head>
<title>Configuration Proxy Methods Inspection</title></head>
<body>
<div>
Reports errors when used proxy methods are used incorrectly.
</div>
<hr>
<strong>Configuration example:</strong><br>
<pre><code lang="java">
@Configuration(proxyBeanMethods = false)
class ProxyMethodsConfiguration {
@Bean
BeanA beanA() {...}

@Bean
public BeanB beanB() {
return new BeanB(beanA()); // @Bean method is called directly in a @Configuration where proxyBeanMethods has been disabled (set to false)
}
}</code>
</pre>
<hr>

<strong>Bean light-mode example:</strong><br>
<pre><code lang="java">
@Component
class BeanLightComponent {
@Bean
public BeanA beanA() {...}

@Bean
public BeanB beanB() {
return new BeanB(beanA()); // Method annotated with @Bean is invoked directly
}
}</code>
</pre>

</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<html lang="en">
<head>
<title>Cache on interface inspection</title>
</head>
<body>
<p>Reports @Cache annotations declared on interface</p>

<p><b>Example:</b></p>
"@Cache annotations shouldn't annotate interfaces" will be reported on 'MyInterface'
"@Cache annotations should annotate concrete methods" will be reported on 'myMethod'
<pre><code lang="java">
@CacheConfig
public interface MyInterface {
@CacheEvict
void myMethod();
}
</code></pre>

</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright © 2024 Explyt Ltd
*
* All rights reserved.
*
* This code and software are the property of Explyt Ltd and are protected by copyright and other intellectual property laws.
*
* You may use this code under the terms of the Explyt Source License Version 1.0 ("License"), if you accept its terms and conditions.
*
* By installing, downloading, accessing, using, or distributing this code, you agree to the terms and conditions of the License.
* If you do not agree to such terms and conditions, you must cease using this code and immediately delete all copies of it.
*
* You may obtain a copy of the License at: https://github.com/explyt/spring-plugin/blob/main/EXPLYT-SOURCE-LICENSE.md
*
* Unauthorized use of this code constitutes a violation of intellectual property rights and may result in legal action.
*/

package com.explyt.spring.core.inspections.java

import com.explyt.spring.test.ExplytInspectionJavaTestCase
import com.explyt.spring.test.TestLibrary
import org.jetbrains.kotlin.test.TestMetadata

class SpringConfigurationProxyBeanMethodsInspectionTest : ExplytInspectionJavaTestCase() {

override val libraries: Array<TestLibrary> = arrayOf(TestLibrary.springContext_6_0_7)

@TestMetadata("configurationProxyMethods")
fun testConfigurationProxyMethods() =
doTest(com.explyt.spring.core.inspections.SpringConfigurationProxyBeanMethodsInspection())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright © 2024 Explyt Ltd
*
* All rights reserved.
*
* This code and software are the property of Explyt Ltd and are protected by copyright and other intellectual property laws.
*
* You may use this code under the terms of the Explyt Source License Version 1.0 ("License"), if you accept its terms and conditions.
*
* By installing, downloading, accessing, using, or distributing this code, you agree to the terms and conditions of the License.
* If you do not agree to such terms and conditions, you must cease using this code and immediately delete all copies of it.
*
* You may obtain a copy of the License at: https://github.com/explyt/spring-plugin/blob/main/EXPLYT-SOURCE-LICENSE.md
*
* Unauthorized use of this code constitutes a violation of intellectual property rights and may result in legal action.
*/

package com.explyt.spring.core.inspections.java

import com.explyt.spring.core.inspections.SpringInterfaceCacheAnnotationsInspection
import com.explyt.spring.test.ExplytInspectionJavaTestCase
import com.explyt.spring.test.TestLibrary
import org.jetbrains.kotlin.test.TestMetadata

class SpringInterfaceCacheAnnotationsInspectionTest : ExplytInspectionJavaTestCase() {

override val libraries: Array<TestLibrary> = arrayOf(TestLibrary.springBootAutoConfigure_3_1_1)

@TestMetadata("cacheOnInterface")
fun testCacheOnInterface() =
doTest(SpringInterfaceCacheAnnotationsInspection())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright © 2024 Explyt Ltd
*
* All rights reserved.
*
* This code and software are the property of Explyt Ltd and are protected by copyright and other intellectual property laws.
*
* You may use this code under the terms of the Explyt Source License Version 1.0 ("License"), if you accept its terms and conditions.
*
* By installing, downloading, accessing, using, or distributing this code, you agree to the terms and conditions of the License.
* If you do not agree to such terms and conditions, you must cease using this code and immediately delete all copies of it.
*
* You may obtain a copy of the License at: https://github.com/explyt/spring-plugin/blob/main/EXPLYT-SOURCE-LICENSE.md
*
* Unauthorized use of this code constitutes a violation of intellectual property rights and may result in legal action.
*/

package com.explyt.spring.core.inspections.kotlin

import com.explyt.spring.core.inspections.SpringConfigurationProxyBeanMethodsInspection
import com.explyt.spring.test.ExplytInspectionKotlinTestCase
import com.explyt.spring.test.TestLibrary
import org.jetbrains.kotlin.test.TestMetadata

class SpringConfigurationProxyBeanMethodsInspectionTest : ExplytInspectionKotlinTestCase() {

override val libraries: Array<TestLibrary> = arrayOf(TestLibrary.springContext_6_0_7)

@TestMetadata("configurationProxyMethods")
fun testConfigurationProxyMethods() = doTest(SpringConfigurationProxyBeanMethodsInspection())
}
Loading

0 comments on commit 7741fae

Please sign in to comment.