Skip to content

Commit 871e0f8

Browse files
committed
Added parameter type checking for DAO methods with @select
1 parent 2ccf5b2 commit 871e0f8

21 files changed

+445
-81
lines changed

src/main/kotlin/org/domaframework/doma/intellij/common/util/DomaClassName.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ enum class DomaClassName(
6060
FLOAT("java.lang.Float"),
6161
BOOLEAN("java.lang.Boolean"),
6262

63+
JAVA_FUNCTION("java.util.function.Function"),
64+
JAVA_COLLECTOR("java.util.stream.Collector"),
65+
JAVA_STREAM("java.util.stream.Stream"),
66+
SELECT_TYPE("org.seasar.doma.SelectType"),
67+
6368
ENTITY("org.seasar.doma.Entity"),
6469
;
6570

@@ -68,8 +73,8 @@ enum class DomaClassName(
6873
fun getGenericParamCanonicalText(vararg genericParas: String): String = "${this.className}<${genericParas.joinToString(", ")}>"
6974

7075
companion object {
71-
fun isOptionalType(paramTypeCanonicalNames: String): Boolean =
72-
paramTypeCanonicalNames in
76+
fun isOptionalWrapperType(paramTypeCanonicalName: String): Boolean =
77+
paramTypeCanonicalName in
7378
listOf(
7479
OPTIONAL_INT.className,
7580
OPTIONAL_DOUBLE.className,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.validation.result
17+
18+
import com.intellij.codeInspection.ProblemsHolder
19+
import com.intellij.openapi.util.TextRange
20+
import com.intellij.psi.PsiElement
21+
import org.domaframework.doma.intellij.bundle.MessageBundle
22+
import org.domaframework.doma.intellij.common.psi.PsiParentClass
23+
24+
class ValidationMethodNotSelectStreamParamResult(
25+
override val identify: PsiElement?,
26+
override val shortName: String = "",
27+
) : ValidationResult(identify, null, shortName) {
28+
override fun setHighlight(
29+
highlightRange: TextRange,
30+
identify: PsiElement,
31+
holder: ProblemsHolder,
32+
parent: PsiParentClass?,
33+
) {
34+
val project = identify.project
35+
holder.registerProblem(
36+
identify,
37+
MessageBundle.message("inspection.invalid.dao.select.param.notFound.strategy.stream"),
38+
problemHighlightType(project, shortName),
39+
highlightRange,
40+
)
41+
}
42+
}

src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodProcedureParamsSupportGenericParamResult.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ValidationMethodProcedureParamsSupportGenericParamResult(
2525
override val identify: PsiElement?,
2626
override val shortName: String = "",
2727
private val paramTypeName: String,
28-
private val procedureAnnotationTypeRequireType: String,
28+
private val requireTypeName: String,
2929
) : ValidationResult(identify, null, shortName) {
3030
override fun setHighlight(
3131
highlightRange: TextRange,
@@ -39,7 +39,7 @@ class ValidationMethodProcedureParamsSupportGenericParamResult(
3939
MessageBundle.message(
4040
"inspection.invalid.dao.procedure.params.support.generic.param",
4141
paramTypeName,
42-
procedureAnnotationTypeRequireType,
42+
requireTypeName,
4343
),
4444
problemHighlightType(project, shortName),
4545
highlightRange,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.validation.result
17+
18+
import com.intellij.codeInspection.ProblemsHolder
19+
import com.intellij.openapi.util.TextRange
20+
import com.intellij.psi.PsiElement
21+
import org.domaframework.doma.intellij.bundle.MessageBundle
22+
import org.domaframework.doma.intellij.common.psi.PsiParentClass
23+
24+
class ValidationMethodSelectStrategyParamResult(
25+
override val identify: PsiElement?,
26+
override val shortName: String = "",
27+
private val selectTypeName: String,
28+
private val requireClassName: String,
29+
) : ValidationResult(identify, null, shortName) {
30+
override fun setHighlight(
31+
highlightRange: TextRange,
32+
identify: PsiElement,
33+
holder: ProblemsHolder,
34+
parent: PsiParentClass?,
35+
) {
36+
val project = identify.project
37+
holder.registerProblem(
38+
identify,
39+
MessageBundle.message("inspection.invalid.dao.select.param.strategy.require.type", selectTypeName, requireClassName),
40+
problemHighlightType(project, shortName),
41+
highlightRange,
42+
)
43+
}
44+
}

src/main/kotlin/org/domaframework/doma/intellij/extension/psi/PsiClassExtension.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,14 @@ fun PsiClass.getClassAnnotation(annotationClassName: String): PsiAnnotation? =
4949
fun PsiClass.isEntity(): Boolean = this.getClassAnnotation(DomaClassName.ENTITY.className) != null
5050

5151
fun PsiClass.isDomain(): Boolean = this.getClassAnnotation(DomaClassName.DOMAIN.className) != null
52+
53+
fun PsiClassType.getSuperType(superClassName: String): PsiClassType? {
54+
var parent: PsiClassType? = this
55+
while (parent != null && !parent.canonicalText.startsWith(superClassName)) {
56+
parent =
57+
parent.superTypes.find { superType ->
58+
superType.canonicalText.startsWith(superClassName)
59+
} as? PsiClassType
60+
}
61+
return parent
62+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.domaframework.doma.intellij.inspection.dao.processor
2+
3+
import com.intellij.psi.PsiAnnotation
4+
import com.intellij.psi.PsiClassType
5+
import com.intellij.psi.PsiField
6+
import com.intellij.psi.PsiReferenceExpression
7+
import com.intellij.psi.PsiType
8+
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
9+
import org.domaframework.doma.intellij.common.psi.PsiTypeChecker
10+
import org.domaframework.doma.intellij.common.util.DomaClassName
11+
import org.domaframework.doma.intellij.extension.getJavaClazz
12+
import org.domaframework.doma.intellij.extension.psi.isDomain
13+
14+
abstract class TypeCheckerProcessor(
15+
psiDaoMethod: PsiDaoMethod,
16+
) {
17+
protected val method = psiDaoMethod.psiMethod
18+
protected val project = method.project
19+
20+
protected fun getAnnotation(fqName: String): PsiAnnotation? = method.annotations.find { it.qualifiedName == fqName }
21+
22+
protected fun getDaoAnnotationOption(
23+
psiAnnotation: PsiAnnotation,
24+
findOptionName: String,
25+
): PsiAnnotation? {
26+
val returningOption =
27+
psiAnnotation.parameterList.attributes
28+
.firstOrNull { param ->
29+
param.name == findOptionName
30+
}?.value as? PsiAnnotation
31+
32+
return returningOption
33+
}
34+
35+
protected fun getDaoPsiReferenceOption(
36+
psiAnnotation: PsiAnnotation,
37+
findOptionName: String,
38+
): PsiField? {
39+
val strategy =
40+
psiAnnotation.parameterList.attributes
41+
.firstOrNull { param ->
42+
param.name == findOptionName
43+
}?.value as? PsiReferenceExpression
44+
45+
val field = strategy?.reference?.resolve() as? PsiField
46+
47+
return field
48+
}
49+
50+
open fun checkParamType(paramType: PsiType): Boolean {
51+
if (PsiTypeChecker.isBaseClassType(paramType)) return true
52+
53+
if (DomaClassName.isOptionalWrapperType(paramType.canonicalText)) {
54+
return true
55+
}
56+
57+
if (DomaClassName.OPTIONAL.isTargetClassNameStartsWith(paramType.canonicalText)) {
58+
val paramClassType = paramType as? PsiClassType ?: return false
59+
val optionalParam = paramClassType.parameters.firstOrNull()
60+
return optionalParam?.let {
61+
val optionalParamClass = project.getJavaClazz(it.canonicalText)
62+
optionalParamClass?.isDomain() == true || PsiTypeChecker.isBaseClassType(it)
63+
} == true
64+
}
65+
66+
val paramClass = project.getJavaClazz(paramType.canonicalText)
67+
return paramClass?.isDomain() == true
68+
}
69+
70+
protected fun checkMapType(paramTypeCanonicalText: String): Boolean {
71+
val mapClassName = paramTypeCanonicalText.replace(" ", "")
72+
val mapExpectedType =
73+
DomaClassName.MAP
74+
.getGenericParamCanonicalText(
75+
DomaClassName.STRING.className,
76+
DomaClassName.OBJECT.className,
77+
).replace(" ", "")
78+
return mapClassName != mapExpectedType
79+
}
80+
}

src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionInOutParamAnnotationTypeChecker.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ import com.intellij.openapi.project.Project
2020
import com.intellij.psi.PsiClassType
2121
import com.intellij.psi.PsiElement
2222
import com.intellij.psi.PsiType
23+
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
2324
import org.domaframework.doma.intellij.common.util.DomaClassName
2425
import org.domaframework.doma.intellij.common.validation.result.ValidationMethodProcedureParamTypeResult
2526
import org.domaframework.doma.intellij.common.validation.result.ValidationMethodProcedureParamsSupportGenericParamResult
2627

2728
class ProcedureFunctionInOutParamAnnotationTypeChecker(
2829
private val annotationType: ProcedureFunctionParamAnnotationType,
29-
) : ProcedureFunctionParamAnnotationTypeChecker() {
30+
psiDaoMethod: PsiDaoMethod,
31+
) : ProcedureFunctionParamAnnotationTypeChecker(psiDaoMethod) {
3032
override fun checkParam(
3133
identifier: PsiElement,
3234
paramType: PsiType,
@@ -56,7 +58,7 @@ class ProcedureFunctionInOutParamAnnotationTypeChecker(
5658
return
5759
}
5860

59-
if (checkParamType(referenceParamType, project)) return
61+
if (checkParamType(referenceParamType)) return
6062
ValidationMethodProcedureParamsSupportGenericParamResult(
6163
identifier,
6264
shortName,

src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionInParamAnnotationTypeChecker.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,20 @@ import com.intellij.codeInspection.ProblemsHolder
1919
import com.intellij.openapi.project.Project
2020
import com.intellij.psi.PsiElement
2121
import com.intellij.psi.PsiType
22+
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
2223
import org.domaframework.doma.intellij.common.validation.result.ValidationMethodParamsProcedureInTypeResult
2324

24-
class ProcedureFunctionInParamAnnotationTypeChecker : ProcedureFunctionParamAnnotationTypeChecker() {
25+
class ProcedureFunctionInParamAnnotationTypeChecker(
26+
psiDaoMethod: PsiDaoMethod,
27+
) : ProcedureFunctionParamAnnotationTypeChecker(psiDaoMethod) {
2528
override fun checkParam(
2629
identifier: PsiElement,
2730
paramType: PsiType,
2831
project: Project,
2932
shortName: String,
3033
holder: ProblemsHolder,
3134
) {
32-
if (checkParamType(paramType, project)) return
35+
if (checkParamType(paramType)) return
3336

3437
ValidationMethodParamsProcedureInTypeResult(
3538
identifier,

src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionParamAnnotationType.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,41 +19,49 @@ import com.intellij.codeInspection.ProblemsHolder
1919
import com.intellij.openapi.project.Project
2020
import com.intellij.psi.PsiElement
2121
import com.intellij.psi.PsiType
22+
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
2223
import org.domaframework.doma.intellij.common.util.DomaClassName
2324

2425
enum class ProcedureFunctionParamAnnotationType(
2526
val fqdn: String,
2627
val requireType: String,
2728
val checkParamType: (
29+
psiDaoMethod: PsiDaoMethod,
2830
identifier: PsiElement,
2931
paramType: PsiType,
3032
project: Project,
3133
shortName: String,
3234
holder: ProblemsHolder,
3335
) -> Unit,
3436
) {
35-
In("org.seasar.doma.In", "", { identifier, paramType, project, shortName, holder ->
36-
ProcedureFunctionInParamAnnotationTypeChecker().checkParam(identifier, paramType, project, shortName, holder)
37+
In("org.seasar.doma.In", "", { psiDaoMethod, identifier, paramType, project, shortName, holder ->
38+
ProcedureFunctionInParamAnnotationTypeChecker(psiDaoMethod).checkParam(identifier, paramType, project, shortName, holder)
3739
}),
3840
InOut(
3941
"org.seasar.doma.InOut",
4042
DomaClassName.REFERENCE.className,
41-
{ identifier, paramType, project, shortName, holder ->
42-
ProcedureFunctionInOutParamAnnotationTypeChecker(InOut).checkParam(identifier, paramType, project, shortName, holder)
43+
{ psiDaoMethod, identifier, paramType, project, shortName, holder ->
44+
ProcedureFunctionInOutParamAnnotationTypeChecker(
45+
InOut,
46+
psiDaoMethod,
47+
).checkParam(identifier, paramType, project, shortName, holder)
4348
},
4449
),
4550
Out(
4651
"org.seasar.doma.Out",
4752
DomaClassName.REFERENCE.className,
48-
{ identifier, paramType, project, shortName, holder ->
49-
ProcedureFunctionInOutParamAnnotationTypeChecker(Out).checkParam(identifier, paramType, project, shortName, holder)
53+
{ psiDaoMethod, identifier, paramType, project, shortName, holder ->
54+
ProcedureFunctionInOutParamAnnotationTypeChecker(
55+
Out,
56+
psiDaoMethod,
57+
).checkParam(identifier, paramType, project, shortName, holder)
5058
},
5159
),
5260
ResultSet(
5361
"org.seasar.doma.ResultSet",
5462
DomaClassName.LIST.className,
55-
{ identifier, paramType, project, shortName, holder ->
56-
ProcedureFunctionResultSetParamAnnotationTypeChecker().checkParam(identifier, paramType, project, shortName, holder)
63+
{ psiDaoMethod, identifier, paramType, project, shortName, holder ->
64+
ProcedureFunctionResultSetParamAnnotationTypeChecker(psiDaoMethod).checkParam(identifier, paramType, project, shortName, holder)
5765
},
5866
),
5967
}

src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionParamAnnotationTypeChecker.kt

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,14 @@ package org.domaframework.doma.intellij.inspection.dao.processor.cheker
1717

1818
import com.intellij.codeInspection.ProblemsHolder
1919
import com.intellij.openapi.project.Project
20-
import com.intellij.psi.PsiClassType
2120
import com.intellij.psi.PsiElement
2221
import com.intellij.psi.PsiType
23-
import org.domaframework.doma.intellij.common.psi.PsiTypeChecker
24-
import org.domaframework.doma.intellij.common.util.DomaClassName
25-
import org.domaframework.doma.intellij.extension.getJavaClazz
26-
import org.domaframework.doma.intellij.extension.psi.isDomain
27-
28-
abstract class ProcedureFunctionParamAnnotationTypeChecker {
29-
open fun checkParamType(
30-
paramType: PsiType,
31-
project: Project,
32-
): Boolean {
33-
if (PsiTypeChecker.isBaseClassType(paramType)) return true
34-
35-
if (DomaClassName.isOptionalType(paramType.canonicalText)) {
36-
return true
37-
}
38-
39-
if (DomaClassName.OPTIONAL.isTargetClassNameStartsWith(paramType.canonicalText)) {
40-
val paramClassType = paramType as? PsiClassType ?: return false
41-
val optionalParam = paramClassType.parameters.firstOrNull()
42-
return optionalParam?.let {
43-
val optionalParamClass = project.getJavaClazz(it.canonicalText)
44-
optionalParamClass?.isDomain() == true || PsiTypeChecker.isBaseClassType(it)
45-
} == true
46-
}
47-
48-
val paramClass = project.getJavaClazz(paramType.canonicalText)
49-
return paramClass?.isDomain() == true
50-
}
22+
import org.domaframework.doma.intellij.common.psi.PsiDaoMethod
23+
import org.domaframework.doma.intellij.inspection.dao.processor.TypeCheckerProcessor
5124

25+
abstract class ProcedureFunctionParamAnnotationTypeChecker(
26+
psiDaoMethod: PsiDaoMethod,
27+
) : TypeCheckerProcessor(psiDaoMethod) {
5228
abstract fun checkParam(
5329
identifier: PsiElement,
5430
paramType: PsiType,

0 commit comments

Comments
 (0)