From 36bc84e209b362b9ea60e166140f7a35e046ad3c Mon Sep 17 00:00:00 2001 From: xterao Date: Mon, 23 Jun 2025 11:30:33 +0900 Subject: [PATCH 1/4] Add return type check processors for Function and Select annotations --- .../inspection/dao/processor/StrategyParam.kt | 29 ++++ .../dao/processor/TypeCheckerProcessor.kt | 9 +- ...ctionAnnotationReturnTypeCheckProcessor.kt | 94 +++++++++++ ...electAnnotationReturnTypeCheckProcessor.kt | 147 ++++++++++++++++++ .../DaoMethodReturnTypeInspectionVisitor.kt | 18 +++ .../messages/DomaToolsBundle.properties | 2 + .../messages/DomaToolsBundle_ja.properties | 6 +- 7 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/StrategyParam.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/FunctionAnnotationReturnTypeCheckProcessor.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/SelectAnnotationReturnTypeCheckProcessor.kt diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/StrategyParam.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/StrategyParam.kt new file mode 100644 index 00000000..9d2aa207 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/StrategyParam.kt @@ -0,0 +1,29 @@ +/* + * Copyright Doma Tools Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.domaframework.doma.intellij.inspection.dao.processor + +import org.domaframework.doma.intellij.common.util.DomaClassName + +class StrategyParam( + val fieldName: String = "", + parentClassName: String?, +) { + private val isSelectType: Boolean = parentClassName == DomaClassName.SELECT_TYPE.className + + fun isStream(): Boolean = fieldName == "STREAM" && isSelectType + + fun isCollect(): Boolean = fieldName == "COLLECT" && isSelectType +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/TypeCheckerProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/TypeCheckerProcessor.kt index af81889e..c422d97e 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/TypeCheckerProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/TypeCheckerProcessor.kt @@ -18,12 +18,14 @@ package org.domaframework.doma.intellij.inspection.dao.processor import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiClassType import com.intellij.psi.PsiField +import com.intellij.psi.PsiParameter import com.intellij.psi.PsiReferenceExpression import com.intellij.psi.PsiType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.psi.PsiTypeChecker import org.domaframework.doma.intellij.common.util.DomaClassName import org.domaframework.doma.intellij.extension.getJavaClazz +import org.domaframework.doma.intellij.extension.psi.getSuperType import org.domaframework.doma.intellij.extension.psi.isDomain abstract class TypeCheckerProcessor( @@ -34,6 +36,11 @@ abstract class TypeCheckerProcessor( protected fun getAnnotation(fqName: String): PsiAnnotation? = method.annotations.find { it.qualifiedName == fqName } + protected fun getMethodParamTargetType(typeName: String): PsiParameter? = + method.parameterList.parameters.find { param -> + (param.type as? PsiClassType)?.getSuperType(typeName) != null + } + protected fun getDaoAnnotationOption( psiAnnotation: PsiAnnotation, findOptionName: String, @@ -90,6 +97,6 @@ abstract class TypeCheckerProcessor( DomaClassName.STRING.className, DomaClassName.OBJECT.className, ).replace(" ", "") - return mapClassName != mapExpectedType + return mapClassName == mapExpectedType } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/FunctionAnnotationReturnTypeCheckProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/FunctionAnnotationReturnTypeCheckProcessor.kt new file mode 100644 index 00000000..cccf8976 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/FunctionAnnotationReturnTypeCheckProcessor.kt @@ -0,0 +1,94 @@ +/* + * Copyright Doma Tools Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.domaframework.doma.intellij.inspection.dao.processor.returntype + +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypes +import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiTypeChecker +import org.domaframework.doma.intellij.common.util.DomaClassName +import org.domaframework.doma.intellij.common.validation.result.ValidationMethodInvalidReturnTypeResult +import org.domaframework.doma.intellij.common.validation.result.ValidationResult +import org.domaframework.doma.intellij.extension.getJavaClazz +import org.domaframework.doma.intellij.extension.psi.isDomain +import org.domaframework.doma.intellij.extension.psi.isEntity + +class FunctionAnnotationReturnTypeCheckProcessor( + psiDaoMethod: PsiDaoMethod, + private val shortName: String, +) : ReturnTypeCheckerProcessor(psiDaoMethod, shortName) { + override fun checkReturnType(): ValidationResult? { + val returnType = method.returnType + if (returnType == null || returnType == PsiTypes.voidType()) return null + + val checkType = + if (DomaClassName.LIST.isTargetClassNameStartsWith(returnType.canonicalText)) { + val listClassType = returnType as? PsiClassType + listClassType?.parameters?.firstOrNull() + } else { + returnType + } + + val checkTypeCanonicalText = checkType?.canonicalText ?: "Unknown" + if (DomaClassName.OPTIONAL.isTargetClassNameStartsWith(checkTypeCanonicalText)) { + val optionalClassType = checkType as? PsiClassType + val optionalParamType = optionalClassType?.parameters?.firstOrNull() + return checkReturnTypeParam(optionalParamType) + } + + return checkReturnTypeParam(checkType) + } + + private fun checkReturnTypeParam(checkType: PsiType?): ValidationResult? { + val identifier = method.nameIdentifier ?: return null + val checkTypeCanonicalText = checkType?.canonicalText ?: "Unknown" + val result = + ValidationMethodInvalidReturnTypeResult( + identifier, + shortName, + checkTypeCanonicalText, + ) + if (checkType == null) return result + + if (DomaClassName.isOptionalWrapperType(checkTypeCanonicalText) || + PsiTypeChecker.isBaseClassType(checkType) + ) { + return null + } + + if (DomaClassName.MAP.isTargetClassNameStartsWith(checkTypeCanonicalText)) { + return if (!checkMapType(checkTypeCanonicalText)) { + result + } else { + null + } + } + + val checkTypeClass = project.getJavaClazz(checkType.canonicalText) + // TODO: 基本クラス、ドメイン、エンティティ、Map、Optional 以外の型チェックを実装 + if (checkTypeClass != null && + ( + checkTypeClass.isDomain() || + checkTypeClass.isEntity() + ) + ) { + return null + } + + return result + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/SelectAnnotationReturnTypeCheckProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/SelectAnnotationReturnTypeCheckProcessor.kt new file mode 100644 index 00000000..8c1ebbb5 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/SelectAnnotationReturnTypeCheckProcessor.kt @@ -0,0 +1,147 @@ +/* + * Copyright Doma Tools Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.domaframework.doma.intellij.inspection.dao.processor.returntype + +import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiType +import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiTypeChecker +import org.domaframework.doma.intellij.common.util.DomaClassName +import org.domaframework.doma.intellij.common.validation.result.ValidationMethodInvalidReturnTypeResult +import org.domaframework.doma.intellij.common.validation.result.ValidationMethodSelectStrategyReturnTypeResult +import org.domaframework.doma.intellij.common.validation.result.ValidationResult +import org.domaframework.doma.intellij.extension.getJavaClazz +import org.domaframework.doma.intellij.extension.psi.isDomain +import org.domaframework.doma.intellij.extension.psi.isEntity +import org.domaframework.doma.intellij.inspection.dao.processor.StrategyParam + +class SelectAnnotationReturnTypeCheckProcessor( + private val psiDaoMethod: PsiDaoMethod, + private val shortName: String, +) : ReturnTypeCheckerProcessor(psiDaoMethod, shortName) { + override fun checkReturnType(): ValidationResult? { + val returnType = method.returnType + if (returnType == null) return null + + val methodAnnotation = getAnnotation(psiDaoMethod.daoType.fqdn) ?: return null + val strategyOpt = getDaoPsiReferenceOption(methodAnnotation, "strategy") + val strategyParam = + if (strategyOpt == null) { + null + } else { + StrategyParam( + strategyOpt.name, + strategyOpt.type.canonicalText, + ) + } + strategyParam?.let { strategy -> + if (strategy.isStream()) { + return checkStream() + } + + if (strategy.isCollect()) { + return checkCollect() + } + } + + if (DomaClassName.JAVA_STREAM.isTargetClassNameStartsWith(returnType.canonicalText)) return null + + val checkType = + if (DomaClassName.LIST.isTargetClassNameStartsWith(returnType.canonicalText)) { + val listClassType = returnType as? PsiClassType + listClassType?.parameters?.firstOrNull() + } else { + returnType + } + + val checkTypeCanonicalText = checkType?.canonicalText ?: "Unknown" + if (DomaClassName.OPTIONAL.isTargetClassNameStartsWith(checkTypeCanonicalText)) { + val optionalClassType = checkType as? PsiClassType + val optionalParamType = optionalClassType?.parameters?.firstOrNull() + return checkReturnTypeParam(optionalParamType) + } + + return checkReturnTypeParam(checkType) + } + + private fun checkReturnTypeParam(checkType: PsiType?): ValidationResult? { + val identifier = method.nameIdentifier ?: return null + val checkTypeCanonicalText = checkType?.canonicalText ?: "Unknown" + val result = + ValidationMethodInvalidReturnTypeResult( + identifier, + shortName, + checkTypeCanonicalText, + ) + if (checkType == null) return result + + if (DomaClassName.isOptionalWrapperType(checkTypeCanonicalText) || + PsiTypeChecker.isBaseClassType(checkType) + ) { + return null + } + + if (DomaClassName.MAP.isTargetClassNameStartsWith(checkTypeCanonicalText)) { + return if (!checkMapType(checkTypeCanonicalText)) { + result + } else { + null + } + } + + val checkTypeClass = project.getJavaClazz(checkType.canonicalText) + if (checkTypeClass != null && + (checkTypeClass.isDomain() || checkTypeClass.isEntity()) + ) { + return null + } + + return result + } + + private fun checkStream(): ValidationResult? { + val function = + getMethodParamTargetType(DomaClassName.JAVA_FUNCTION.className) ?: return null + + val functionClassType = (function.type as? PsiClassType) + val functionParams = functionClassType?.parameters ?: return null + if (functionParams.size < 2) return null + val functionResultParam = functionParams[1] ?: return null + + if (functionResultParam == method.returnType) return null + return ValidationMethodSelectStrategyReturnTypeResult( + method.nameIdentifier, + shortName, + DomaClassName.JAVA_FUNCTION.className, + ) + } + + private fun checkCollect(): ValidationResult? { + val collection = getMethodParamTargetType(DomaClassName.JAVA_COLLECTOR.className) ?: return null + val collectorParamClassType = (collection.type as? PsiClassType) + val collectorParams = collectorParamClassType?.parameters ?: return null + if (collectorParams.size < 3) return null + + val collectorTargetParam = collectorParams[2] + if (collectorTargetParam == method.returnType) return null + + return ValidationMethodSelectStrategyReturnTypeResult( + method.nameIdentifier, + shortName, + DomaClassName.JAVA_COLLECTOR.className, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/visitor/DaoMethodReturnTypeInspectionVisitor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/visitor/DaoMethodReturnTypeInspectionVisitor.kt index 90aa0b24..48c90a7c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/visitor/DaoMethodReturnTypeInspectionVisitor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/visitor/DaoMethodReturnTypeInspectionVisitor.kt @@ -23,10 +23,12 @@ import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType import org.domaframework.doma.intellij.inspection.dao.processor.returntype.BatchAnnotationReturnTypeCheckProcessor +import org.domaframework.doma.intellij.inspection.dao.processor.returntype.FunctionAnnotationReturnTypeCheckProcessor import org.domaframework.doma.intellij.inspection.dao.processor.returntype.MultiInsertAnnotationReturnTypeCheckProcessor import org.domaframework.doma.intellij.inspection.dao.processor.returntype.ProcedureAnnotationReturnTypeCheckProcessor import org.domaframework.doma.intellij.inspection.dao.processor.returntype.ReturnTypeCheckerProcessor import org.domaframework.doma.intellij.inspection.dao.processor.returntype.ScriptAnnotationReturnTypeCheckProcessor +import org.domaframework.doma.intellij.inspection.dao.processor.returntype.SelectAnnotationReturnTypeCheckProcessor import org.domaframework.doma.intellij.inspection.dao.processor.returntype.SqlProcessorAnnotationReturnTypeCheckProcessor import org.domaframework.doma.intellij.inspection.dao.processor.returntype.UpdateAnnotationReturnTypeCheckProcessor @@ -47,6 +49,12 @@ class DaoMethodReturnTypeInspectionVisitor( private fun getReturnTypeCheckProcessor(psiDaoMethod: PsiDaoMethod): ReturnTypeCheckerProcessor? = when (psiDaoMethod.daoType) { + DomaAnnotationType.Select -> { + SelectAnnotationReturnTypeCheckProcessor( + psiDaoMethod, + this.shortName, + ) + } DomaAnnotationType.Insert, DomaAnnotationType.Update, DomaAnnotationType.Delete -> { UpdateAnnotationReturnTypeCheckProcessor(psiDaoMethod, this.shortName) } @@ -58,17 +66,20 @@ class DaoMethodReturnTypeInspectionVisitor( DomaAnnotationType.Procedure -> { ProcedureAnnotationReturnTypeCheckProcessor(psiDaoMethod, this.shortName) } + DomaAnnotationType.SqlProcessor -> { SqlProcessorAnnotationReturnTypeCheckProcessor( psiDaoMethod, this.shortName, ) } + DomaAnnotationType.MultiInsert -> MultiInsertAnnotationReturnTypeCheckProcessor( psiDaoMethod, this.shortName, ) + DomaAnnotationType.Script -> { ScriptAnnotationReturnTypeCheckProcessor( psiDaoMethod, @@ -76,6 +87,13 @@ class DaoMethodReturnTypeInspectionVisitor( ) } + DomaAnnotationType.Function -> { + FunctionAnnotationReturnTypeCheckProcessor( + psiDaoMethod, + this.shortName, + ) + } + else -> null } } diff --git a/src/main/resources/messages/DomaToolsBundle.properties b/src/main/resources/messages/DomaToolsBundle.properties index be8c2af3..a6f76fab 100644 --- a/src/main/resources/messages/DomaToolsBundle.properties +++ b/src/main/resources/messages/DomaToolsBundle.properties @@ -16,6 +16,8 @@ inspection.invalid.dao.duplicate=An element name that is a duplicate of an eleme inspection.invalid.sql.notFound.customFunction=Custom function [{0}] not found in class [{1}] inspection.invalid.sql.notFound.expressionClass=An invalid ExpressionFunctions implementation class is configured in doma.compile.config inspection.invalid.dao.returnType.psiTypes=The return type must be "{0}" +inspection.invalid.dao.returnType.invalid=The return type {0} is invalid +inspection.invalid.dao.select.returnType.strategy=The return type must match RESULT of the "{0}" type parameter inspection.invalid.dao.update.returnType.returning=When "returning = @Returning" is specified, the return type must be "{0}" or Optional<{0}> inspection.invalid.dao.sqlProcessor.returnType.returning=The return type "{0}" is not the same as the third type argument "{1}" of BiFunction inspection.invalid.dao.multiInsert.returnType.returning=When "returning = @Returning" is specified, the return type must be List<{0}> diff --git a/src/main/resources/messages/DomaToolsBundle_ja.properties b/src/main/resources/messages/DomaToolsBundle_ja.properties index 1337fa39..21136cab 100644 --- a/src/main/resources/messages/DomaToolsBundle_ja.properties +++ b/src/main/resources/messages/DomaToolsBundle_ja.properties @@ -19,7 +19,7 @@ inspection.invalid.dao.returnType.psiTypes=\u623B\u308A\u5024\u306F"{0}"\u3067\u inspection.invalid.dao.update.returnType.returning=\u300Creturning = @Returning\u300D\u304C\u6307\u5B9A\u3055\u308C\u305F\u5834\u5408\u3001\u623B\u308A\u5024\u306F"{0}"\u307E\u305F\u306F Optional<{0}>\u3067\u306A\u3051\u308C\u3070\u306A\u308A\u307E\u305B\u3093 inspection.invalid.dao.sqlProcessor.returnType.returning=\u623B\u308A\u5024"{0}"\u306F BiFunction \u306E 3 \u756A\u76EE\u306E\u578B\u5F15\u6570"{1}"\u3068\u540C\u3058\u3067\u306A\u3051\u308C\u3070\u306A\u308A\u307E\u305B\u3093 inspection.invalid.dao.multiInsert.returnType.returning=\u300Creturning = @Returning\u300D\u304C\u6307\u5B9A\u3055\u308C\u305F\u5834\u5408\u3001\u623B\u308A\u5024\u306F List<{0}>\u3067\u306A\u3051\u308C\u3070\u306A\u308A\u307E\u305B\u3093 -inspection.invalid.dao.select.param.strategy.require.type=\u0040\u0053\u0065\u006C\u0065\u0063\u0074\u3067\u0073\u0074\u0072\u0061\u0074\u0065\u0067\u0079\u30AA\u30D7\u30B7\u30E7\u30F3\u306B\u0053\u0065\u006C\u0065\u0063\u0074\u0054\u0079\u0070\u0065\u002E{0}\u3092\u6307\u5B9A\u3059\u308B\u5834\u5408\u3001\u30E1\u30BD\u30C3\u30C9\u306B{1}\u578B\u306E\u30D1\u30E9\u30E1\u30FC\u30BF\u304C\u5FC5\u8981\u3067\u3059 +inspection.invalid.dao.select.param.strategy.require.type=\u0040\u0053\u0065\u006C\u0065\u0063\u0074\u3067\u0073\u0074\u0072\u0061\u0074\u0065\u0067\u0079\u30AA\u30D7\u30B7\u30E7\u30F3\u306B\u0053\u0065\u006C\u0065\u0063\u0074\u0054\u0079\u0070\u0065\u002E\u007B\u0030\u007D\u3092\u6307\u5B9A\u3059\u308B\u5834\u5408\u3001\u30E1\u30BD\u30C3\u30C9\u306B\u007B\u0031\u007D\u578B\u306E\u30D1\u30E9\u30E1\u30FC\u30BF\u304C\u5FC5\u8981\u3067\u3059 inspection.invalid.dao.select.param.notFound.strategy.stream=\u300Cjava.util.function.Function\u300D\u30D1\u30E9\u30E1\u30FC\u30BF\u3092\u4F7F\u7528\u3059\u308B\u5834\u5408\u3001@Select\u306E\u6226\u7565\u8981\u7D20\u306BSelectStrategyType.STREAM\u3092\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 inspection.invalid.dao.returnType.immutable=@{0}\u304C\u4ED8\u4E0E\u3055\u308C\u305F\u30E1\u30BD\u30C3\u30C9\u304C\u4E0D\u5909\u306A\u30A8\u30F3\u30C6\u30A3\u30C6\u30A3\u30AF\u30E9\u30B9\u3092\u5F15\u6570\u306B\u6301\u3064\u5834\u5408\u3001\u623B\u308A\u5024\u306F {1}<{2}>\u3067\u306A\u3051\u308C\u3070\u306A\u308A\u307E\u305B\u3093 inspection.invalid.dao.params.count=\u5F15\u6570\u306E\u6570\u306F1\u3064\u3067\u306A\u3051\u308C\u3070\u306A\u308A\u307E\u305B\u3093 @@ -30,4 +30,6 @@ inspection.invalid.dao.params.require.type=@{0}\u304C\u4ED8\u4E0E\u3055\u308C\u3 inspection.invalid.dao.params.require.iterable.entity=\u5F15\u6570\u306F\u30A8\u30F3\u30C6\u30A3\u30C6\u30A3\u30AF\u30E9\u30B9\u3092\u578B\u5F15\u6570\u306B\u6301\u3064Iterable\u306E\u30B5\u30D6\u30AF\u30E9\u30B9\u3067\u306A\u3051\u308C\u3070\u306A\u308A\u307E\u305B\u3093 inspection.invalid.dao.procedure.params.require.annotation=@Function\u307E\u305F\u306F@Procedure\u304C\u4ED8\u4E0E\u3055\u308C\u305F\u30E1\u30BD\u30C3\u30C9\u306E\u5F15\u6570\u306F@In\u3001@InOut\u3001@Out\u3001@ResultSet\u306E\u3044\u305A\u308C\u304B\u3092\u4ED8\u4E0E\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 inspection.invalid.dao.procedure.params.in.type=@In\u304C\u4ED8\u4E0E\u3055\u308C\u305F\u5F15\u6570\u306E\u578B\u3068\u3057\u3066{0}\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u307E\u305B\u3093 -inspection.invalid.dao.procedure.params.support.generic.param="{0}"\u306F"{1}"\u306E\u578B\u5F15\u6570\u3068\u3057\u3066\u4E0D\u6B63\u3067\u3059 \ No newline at end of file +inspection.invalid.dao.procedure.params.support.generic.param="{0}"\u306F"{1}"\u306E\u578B\u5F15\u6570\u3068\u3057\u3066\u4E0D\u6B63\u3067\u3059 +inspection.invalid.dao.returnType.invalid=\u623B\u308A\u5024\u0020\u0020"{0}"\u0020\u306F\u4E0D\u6B63\u3067\u3059 +inspection.invalid.dao.select.returnType.strategy=\u623B\u308A\u5024\u306F\u300C{0}\u300D\u306E\u578B\u30D1\u30E9\u30E1\u30FC\u30BF\u306ERESULT\u3068\u4E00\u81F4\u3057\u3066\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 From a28f638e8075445fe6c6ab27d5fa360857af1cd0 Mon Sep 17 00:00:00 2001 From: xterao Date: Mon, 23 Jun 2025 11:31:46 +0900 Subject: [PATCH 2/4] Refactor DAO return type tests and add new test cases for Function and Select annotations --- ...ctionAnnotationReturnTypeCheckProcessor.kt | 1 - .../doma/intellij/DomaSqlTest.kt | 11 ++ ...AnnotationReturnTypeCheckInspectionTest.kt | 39 ++++-- .../doma/example/collector/HogeCollector.java | 31 +++++ .../paramtype/SelectParamTestDao.java | 2 +- .../BatchReturnTypeTestDao.java} | 4 +- .../returntype/FunctionReturnTypeTestDao.java | 76 ++++++++++ .../MultiInsertReturnTypeTestDao.java} | 4 +- .../ProcedureReturnTypeTestDao.java} | 4 +- .../returntype/SelectReturnTypeTestDao.java | 130 ++++++++++++++++++ .../SqlProcessorReturnTypeTestDao.java} | 4 +- .../UpdateReturnTypeTestDao.java} | 4 +- .../java/doma/example/domain/Hiredate.java | 8 ++ 13 files changed, 293 insertions(+), 25 deletions(-) create mode 100644 src/test/testData/src/main/java/doma/example/collector/HogeCollector.java rename src/test/testData/src/main/java/doma/example/dao/inspection/{BatchReturnTypeDao.java => returntype/BatchReturnTypeTestDao.java} (87%) create mode 100644 src/test/testData/src/main/java/doma/example/dao/inspection/returntype/FunctionReturnTypeTestDao.java rename src/test/testData/src/main/java/doma/example/dao/inspection/{MultiInsertReturnTypeDao.java => returntype/MultiInsertReturnTypeTestDao.java} (91%) rename src/test/testData/src/main/java/doma/example/dao/inspection/{ProcedureReturnTypeDao.java => returntype/ProcedureReturnTypeTestDao.java} (67%) create mode 100644 src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SelectReturnTypeTestDao.java rename src/test/testData/src/main/java/doma/example/dao/inspection/{SqlProcessorReturnTypeDao.java => returntype/SqlProcessorReturnTypeTestDao.java} (89%) rename src/test/testData/src/main/java/doma/example/dao/inspection/{UpdateReturnTypeDao.java => returntype/UpdateReturnTypeTestDao.java} (90%) create mode 100644 src/test/testData/src/main/java/doma/example/domain/Hiredate.java diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/FunctionAnnotationReturnTypeCheckProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/FunctionAnnotationReturnTypeCheckProcessor.kt index cccf8976..349c2df1 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/FunctionAnnotationReturnTypeCheckProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/FunctionAnnotationReturnTypeCheckProcessor.kt @@ -79,7 +79,6 @@ class FunctionAnnotationReturnTypeCheckProcessor( } val checkTypeClass = project.getJavaClazz(checkType.canonicalText) - // TODO: 基本クラス、ドメイン、エンティティ、Map、Optional 以外の型チェックを実装 if (checkTypeClass != null && ( checkTypeClass.isDomain() || diff --git a/src/test/kotlin/org/domaframework/doma/intellij/DomaSqlTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/DomaSqlTest.kt index a0c69321..42ab6f5c 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/DomaSqlTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/DomaSqlTest.kt @@ -169,6 +169,17 @@ open class DomaSqlTest : LightJavaCodeInsightFixtureTestCase() { ) } + protected fun addOtherJavaFile( + packageName: String, + fileName: String, + ) { + val file = File("$testDataPath/$sourceRoot/$packagePath/$packageName/$fileName") + myFixture.addFileToProject( + "main/$sourceRoot/$packagePath/$packageName/$fileName", + file.readText(), + ) + } + fun addResourceEmptySqlFile(vararg sqlFileNames: String) { for (sqlFileName in sqlFileNames) { myFixture.addFileToProject( diff --git a/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationReturnTypeCheckInspectionTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationReturnTypeCheckInspectionTest.kt index c59426cc..71d5ad6c 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationReturnTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationReturnTypeCheckInspectionTest.kt @@ -24,47 +24,60 @@ import org.domaframework.doma.intellij.inspection.dao.inspector.DaoMethodReturnT class AnnotationReturnTypeCheckInspectionTest : DomaSqlTest() { private val testDaoNames = listOf( - "UpdateReturnTypeDao", - "BatchReturnTypeDao", - "MultiInsertReturnTypeDao", - "SqlProcessorReturnTypeDao", - "ProcedureReturnTypeDao", + "SelectReturnTypeTestDao", + "UpdateReturnTypeTestDao", + "BatchReturnTypeTestDao", + "MultiInsertReturnTypeTestDao", + "SqlProcessorReturnTypeTestDao", + "ProcedureReturnTypeTestDao", + "FunctionReturnTypeTestDao", ) - private val daoPackage = "inspection" + private val daoPackage = "inspection/returntype" override fun setUp() { super.setUp() testDaoNames.forEach { daoName -> - addDaoJavaFile("inspection/$daoName.java") + addDaoJavaFile("$daoPackage/$daoName.java") } - // Entity classes addEntityJavaFile("Packet.java") addEntityJavaFile("Pckt.java") + addOtherJavaFile("domain", "Hiredate.java") + addOtherJavaFile("collector", "HogeCollector.java") myFixture.enableInspections(DaoMethodReturnTypeInspection()) } + fun testSelectReturnTypeCheckProcessor() { + val dao = findDaoClass("$daoPackage.SelectReturnTypeTestDao") + myFixture.testHighlighting(false, false, false, dao.containingFile.virtualFile) + } + fun testUpdateAnnotationReturnTypeCheckProcessor() { - val dao = findDaoClass("$daoPackage.UpdateReturnTypeDao") + val dao = findDaoClass("$daoPackage.UpdateReturnTypeTestDao") myFixture.testHighlighting(false, false, false, dao.containingFile.virtualFile) } fun testBatchAnnotationReturnTypeCheckProcessor() { - val dao = findDaoClass("$daoPackage.BatchReturnTypeDao") + val dao = findDaoClass("$daoPackage.BatchReturnTypeTestDao") myFixture.testHighlighting(false, false, false, dao.containingFile.virtualFile) } fun testMultiInsertAnnotationReturnTypeCheckProcessor() { - val dao = findDaoClass("$daoPackage.MultiInsertReturnTypeDao") + val dao = findDaoClass("$daoPackage.MultiInsertReturnTypeTestDao") myFixture.testHighlighting(false, false, false, dao.containingFile.virtualFile) } fun testSqlProcessorAnnotationReturnTypeCheckProcessor() { - val dao = findDaoClass("$daoPackage.SqlProcessorReturnTypeDao") + val dao = findDaoClass("$daoPackage.SqlProcessorReturnTypeTestDao") myFixture.testHighlighting(false, false, false, dao.containingFile.virtualFile) } fun testProcedureAnnotationReturnTypeCheckProcessor() { - val dao = findDaoClass("$daoPackage.ProcedureReturnTypeDao") + val dao = findDaoClass("$daoPackage.ProcedureReturnTypeTestDao") + myFixture.testHighlighting(false, false, false, dao.containingFile.virtualFile) + } + + fun testFunctionAnnotationReturnTypeCheckProcessor() { + val dao = findDaoClass("$daoPackage.FunctionReturnTypeTestDao") myFixture.testHighlighting(false, false, false, dao.containingFile.virtualFile) } } diff --git a/src/test/testData/src/main/java/doma/example/collector/HogeCollector.java b/src/test/testData/src/main/java/doma/example/collector/HogeCollector.java new file mode 100644 index 00000000..9d4e342f --- /dev/null +++ b/src/test/testData/src/main/java/doma/example/collector/HogeCollector.java @@ -0,0 +1,31 @@ +package doma.example.collector; + +import java.util.stream.Collector; + +public class HogeCollector implements Collector { + + @Override + public Supplier supplier() { + return null; + } + + @Override + public BiConsumer accumulator() { + return null; + } + + @Override + public BinaryOperator combiner() { + return null; + } + + @Override + public Function finisher() { + return null; + } + + @Override + public Set characteristics() { + return null; + } + } \ No newline at end of file diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/paramtype/SelectParamTestDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/paramtype/SelectParamTestDao.java index b3bb11b8..48c77ba3 100644 --- a/src/test/testData/src/main/java/doma/example/dao/inspection/paramtype/SelectParamTestDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/paramtype/SelectParamTestDao.java @@ -51,7 +51,7 @@ public interface SelectParamTestDao { @Select(strategy = SelectType.COLLECT) @Sql("select * from emp where salary > /* salary */0") - Optional selectCollectInValidParamResult(BigDecimal salary,Collector> collector); + Optional selectCollectOptionalParamResult(BigDecimal salary,Collector> collector); @Select(strategy = SelectType.COLLECT) @Sql("select * from emp where salary > /* salary */0") diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/BatchReturnTypeDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/BatchReturnTypeTestDao.java similarity index 87% rename from src/test/testData/src/main/java/doma/example/dao/inspection/BatchReturnTypeDao.java rename to src/test/testData/src/main/java/doma/example/dao/inspection/returntype/BatchReturnTypeTestDao.java index b5917b06..2e64a396 100644 --- a/src/test/testData/src/main/java/doma/example/dao/inspection/BatchReturnTypeDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/BatchReturnTypeTestDao.java @@ -1,4 +1,4 @@ -package doma.example.dao.inspection; +package doma.example.dao.inspection.returntype; import org.seasar.doma.*; import org.seasar.doma.jdbc.BatchResult; @@ -6,7 +6,7 @@ import java.util.List; @Dao -public interface BatchReturnTypeDao { +public interface BatchReturnTypeTestDao { @BatchInsert int[] batchInsertReturnsIntArray(List e); diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/FunctionReturnTypeTestDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/FunctionReturnTypeTestDao.java new file mode 100644 index 00000000..c48f6617 --- /dev/null +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/FunctionReturnTypeTestDao.java @@ -0,0 +1,76 @@ +package doma.example.dao.inspection.returntype; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.seasar.doma.Dao; +import org.seasar.doma.Function; +import org.seasar.doma.In; +import org.seasar.doma.InOut; +import org.seasar.doma.Out; +import org.seasar.doma.ResultSet; +import doma.example.entity.*; +import org.seasar.doma.jdbc.Reference; + +@Dao +public interface FunctionReturnTypeTestDao { + + @Function + int primitiveFunction(@In int id); + + @Function + void voidFunction(@In int id); + + @Function + String executeFunction( + @In Integer arg1, @InOut Reference arg2, @Out Reference arg3); + + @Function + Packet entityFunction( + @In Packet arg1, @InOut Reference arg2, @Out Reference arg3); + + @Function + List listFunction(@ResultSet List arg1); + + @Function + List immutableEntityListFunction(@ResultSet List arg1); + + @Function + List entityListFunction(@ResultSet List arg1); + + @Function + Optional optionalFunction( + @In Optional arg1, + @InOut Reference> arg2, + @Out Reference> arg3); + + @Function + List> listOptionalFunction(@ResultSet List> arg1); + + @Function + List> entityOptionalListFunction(@ResultSet List> arg1); + + @Function + Map mapFunction(@ResultSet List> arg1); + + @Function + Optional> mapOptionalFunction(@ResultSet List> arg1); + + @Function + List> mapListFunction(@ResultSet List> arg1); + + @Function + List>> mapOptionalListFunction(@ResultSet List> arg1); + + @Function + Optional enumFunction( + @In Optional arg1, + @InOut Reference> arg2, + @Out Reference> arg3); + + @Function + List> listEnumFunction(@ResultSet List> arg1); + + enum MyEnum {} + +} diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/MultiInsertReturnTypeDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/MultiInsertReturnTypeTestDao.java similarity index 91% rename from src/test/testData/src/main/java/doma/example/dao/inspection/MultiInsertReturnTypeDao.java rename to src/test/testData/src/main/java/doma/example/dao/inspection/returntype/MultiInsertReturnTypeTestDao.java index 3e49d110..ac3c79ad 100644 --- a/src/test/testData/src/main/java/doma/example/dao/inspection/MultiInsertReturnTypeDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/MultiInsertReturnTypeTestDao.java @@ -1,4 +1,4 @@ -package doma.example.dao.inspection; +package doma.example.dao.inspection.returntype; import org.seasar.doma.*; import org.seasar.doma.jdbc.MultiResult; @@ -6,7 +6,7 @@ import java.util.List; @Dao -public interface MultiInsertReturnTypeDao { +public interface MultiInsertReturnTypeTestDao { @MultiInsert int multiInsertReturnsInt(List e); diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/ProcedureReturnTypeDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/ProcedureReturnTypeTestDao.java similarity index 67% rename from src/test/testData/src/main/java/doma/example/dao/inspection/ProcedureReturnTypeDao.java rename to src/test/testData/src/main/java/doma/example/dao/inspection/returntype/ProcedureReturnTypeTestDao.java index 9fd69631..ebe4f2b3 100644 --- a/src/test/testData/src/main/java/doma/example/dao/inspection/ProcedureReturnTypeDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/ProcedureReturnTypeTestDao.java @@ -1,9 +1,9 @@ -package doma.example.dao.inspection; +package doma.example.dao.inspection.returntype; import org.seasar.doma.*; @Dao -public interface ProcedureReturnTypeDao { +public interface ProcedureReturnTypeTestDao { @Procedure void callProcedureReturnsVoid(); @Procedure diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SelectReturnTypeTestDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SelectReturnTypeTestDao.java new file mode 100644 index 00000000..dfb8326f --- /dev/null +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SelectReturnTypeTestDao.java @@ -0,0 +1,130 @@ +package doma.example.dao.inspection.returntype; + +import java.math.BigDecimal; +import org.seasar.doma.*; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Stream; +import org.seasar.doma.*; +import org.seasar.doma.jdbc.SelectOptions; +import org.seasar.doma.message.Message; +import doma.example.entity.*; +import doma.example.domain.*; +import doma.example.collector.*; + +@Dao +public interface SelectReturnTypeTestDao { + + @Select + void selectVoid(Employee e); + + @Select + List selectByExample(Employee e); + + @Select + List selectWithOptionalOrderBy(String employeeName, String orderBy); + + @Select + Employee selectById(Integer employeeId); + + @Select + Employee selectById(Integer employeeId,SelectOptions options); + + @Sql("select * from EMPLOYEE where EMPLOYEE_NAME in /*names*/('aaa', 'bbb')") + @Select + List selectByNames(List names); + + @Select(mapKeyNaming = MapKeyNamingType.CAMEL_CASE) + Map selectByIdAsMap(Integer employeeId); + + @Select(mapKeyNaming = MapKeyNamingType.CAMEL_CASE) + List> selectAllAsMapList(); + + @Select(strategy = SelectType.STREAM, mapKeyNaming = MapKeyNamingType.CAMEL_CASE) + R selectAllAsMapList(Function>, R> mapper); + + @Select(strategy = SelectType.STREAM, mapKeyNaming = MapKeyNamingType.CAMEL_CASE) + R selectAllAsMapList(Function>, R> mapper,SelectOptions options); + + @Select(strategy = SelectType.STREAM, mapKeyNaming = MapKeyNamingType.CAMEL_CASE) + String selectStreamInvalid(Function>, Integer> mapper,SelectOptions options); + + @Select + List selectByNamePrefix(String employeeName); + + @Select + List selectByNameInfix(String employeeName); + + @Select + List selectByNameSuffix(String employeeName); + + @Select + List selectAll(); + + @Select + List selectAll(SelectOptions options); + + @Select + List selectDistinctAll(SelectOptions options); + + @Select(ensureResultMapping = true) + List selectOnlyNameWithMappingCheck(); + + @Select(ensureResultMapping = false) + List selectOnlyNameWithoutMappingCheck(); + + @Select + List selectByInterface(Hiredate hiredate); + + @Select(strategy = SelectType.STREAM) + R streamAll(Function, R> mapper); + + @Select(strategy = SelectType.STREAM) + R streamAll(Function, R> mapper,SelectOptions options); + + @Select(strategy = SelectType.STREAM) + R streamAllSalary(Function, R> mapper); + + @Select(strategy = SelectType.STREAM) + R streamAllSalary(Function, R> mapper,SelectOptions options); + + @Select(strategy = SelectType.STREAM) + R streamBySalary(BigDecimal salary, Function, R> mapper); + + @Select(strategy = SelectType.COLLECT) + R collectAll(Collector collector); + + @Select + @Suppress(messages = {Message.DOMA4274}) + Stream streamAll(); + + @Select + @Suppress(messages = {Message.DOMA4274}) + Stream streamAll(SelectOptions options); + + @Select + @Suppress(messages = {Message.DOMA4274}) + Stream streamBySalary(BigDecimal salary); + + @Select(strategy = SelectType.COLLECT) + Integer selectCollector(Integer id, String name, Collector collector); + + @Select(strategy = SelectType.COLLECT) + R selectCollectorR(Integer id, Collector collector); + + @Select(strategy = SelectType.COLLECT) + Pckt selectWithOutCollector(BigDecimal salary, Packet packet); + + @Select(strategy = SelectType.COLLECT) + R select(Collector collector); + + @Select(strategy = SelectType.COLLECT) + String selectWithHogeCollector(HogeCollector collector); + + @Select(strategy = SelectType.COLLECT, mapKeyNaming = MapKeyNamingType.CAMEL_CASE) + R selectByIdAsMap(Integer id, Collector, ?, R> collector); + + +} diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/SqlProcessorReturnTypeDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SqlProcessorReturnTypeTestDao.java similarity index 89% rename from src/test/testData/src/main/java/doma/example/dao/inspection/SqlProcessorReturnTypeDao.java rename to src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SqlProcessorReturnTypeTestDao.java index b74f4da3..6a3b75b5 100644 --- a/src/test/testData/src/main/java/doma/example/dao/inspection/SqlProcessorReturnTypeDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SqlProcessorReturnTypeTestDao.java @@ -1,4 +1,4 @@ -package doma.example.dao.inspection; +package doma.example.dao.inspection.returntype; import org.seasar.doma.Dao; import org.seasar.doma.SqlProcessor; @@ -9,7 +9,7 @@ import doma.example.entity.*; @Dao -public interface SqlProcessorReturnTypeDao { +public interface SqlProcessorReturnTypeTestDao { @SqlProcessor R processSqlReturnsR(BiFunction handler); diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/UpdateReturnTypeDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/UpdateReturnTypeTestDao.java similarity index 90% rename from src/test/testData/src/main/java/doma/example/dao/inspection/UpdateReturnTypeDao.java rename to src/test/testData/src/main/java/doma/example/dao/inspection/returntype/UpdateReturnTypeTestDao.java index 7e6b505d..6f5ea661 100644 --- a/src/test/testData/src/main/java/doma/example/dao/inspection/UpdateReturnTypeDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/UpdateReturnTypeTestDao.java @@ -1,4 +1,4 @@ -package doma.example.dao.inspection; +package doma.example.dao.inspection.returntype; import org.seasar.doma.*; import org.seasar.doma.jdbc.Result; @@ -6,7 +6,7 @@ import java.util.List; @Dao -public interface UpdateReturnTypeDao { +public interface UpdateReturnTypeTestDao { @Insert int insertReturnsInt(Packet e); diff --git a/src/test/testData/src/main/java/doma/example/domain/Hiredate.java b/src/test/testData/src/main/java/doma/example/domain/Hiredate.java new file mode 100644 index 00000000..9ed0fa93 --- /dev/null +++ b/src/test/testData/src/main/java/doma/example/domain/Hiredate.java @@ -0,0 +1,8 @@ +package doma.example.domain; + +import java.sql.Date; + +public interface Hiredate { + + public Date getValue(); +} From 712849b7d6d42f449488422a4b9fd27844c736ee Mon Sep 17 00:00:00 2001 From: xterao Date: Mon, 23 Jun 2025 11:39:20 +0900 Subject: [PATCH 3/4] Refactor validation result classes for parameter type checks in DAO methods --- ...ValidationMethodInvalidReturnTypeResult.kt | 43 +++++++++++++++++ ...nMethodParamsSupportGenericParamResult.kt} | 2 +- ...lidationMethodSelectStrategyParamResult.kt | 10 ++-- ...ionMethodSelectStrategyReturnTypeResult.kt | 46 +++++++++++++++++++ ...FunctionInOutParamAnnotationTypeChecker.kt | 6 +-- ...tionResultSetParamAnnotationTypeChecker.kt | 8 ++-- .../SelectParamTypeCheckProcessor.kt | 29 +++--------- .../returntype/ReturnTypeCheckerProcessor.kt | 23 ++-------- 8 files changed, 114 insertions(+), 53 deletions(-) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodInvalidReturnTypeResult.kt rename src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/{ValidationMethodProcedureParamsSupportGenericParamResult.kt => ValidationMethodParamsSupportGenericParamResult.kt} (96%) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodSelectStrategyReturnTypeResult.kt diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodInvalidReturnTypeResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodInvalidReturnTypeResult.kt new file mode 100644 index 00000000..f57b3618 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodInvalidReturnTypeResult.kt @@ -0,0 +1,43 @@ +/* + * Copyright Doma Tools Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.domaframework.doma.intellij.common.validation.result + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import org.domaframework.doma.intellij.bundle.MessageBundle +import org.domaframework.doma.intellij.common.psi.PsiParentClass + +class ValidationMethodInvalidReturnTypeResult( + override val identify: PsiElement?, + override val shortName: String = "", + private val returnTypeName: String, +) : ValidationResult(identify, null, shortName) { + override fun setHighlight( + highlightRange: TextRange, + identify: PsiElement, + holder: ProblemsHolder, + parent: PsiParentClass?, + ) { + val project = identify.project + holder.registerProblem( + identify, + MessageBundle.message("inspection.invalid.dao.returnType.invalid", returnTypeName), + problemHighlightType(project, shortName), + highlightRange, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodProcedureParamsSupportGenericParamResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodParamsSupportGenericParamResult.kt similarity index 96% rename from src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodProcedureParamsSupportGenericParamResult.kt rename to src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodParamsSupportGenericParamResult.kt index 88c97348..805bff0c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodProcedureParamsSupportGenericParamResult.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodParamsSupportGenericParamResult.kt @@ -21,7 +21,7 @@ import com.intellij.psi.PsiElement import org.domaframework.doma.intellij.bundle.MessageBundle import org.domaframework.doma.intellij.common.psi.PsiParentClass -class ValidationMethodProcedureParamsSupportGenericParamResult( +class ValidationMethodParamsSupportGenericParamResult( override val identify: PsiElement?, override val shortName: String = "", private val paramTypeName: String, diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodSelectStrategyParamResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodSelectStrategyParamResult.kt index 67f83017..7f5ffab7 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodSelectStrategyParamResult.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodSelectStrategyParamResult.kt @@ -24,8 +24,8 @@ import org.domaframework.doma.intellij.common.psi.PsiParentClass class ValidationMethodSelectStrategyParamResult( override val identify: PsiElement?, override val shortName: String = "", - private val selectTypeName: String, - private val requireClassName: String, + private val genericTypeName: String, + private val parentParamTypeName: String, ) : ValidationResult(identify, null, shortName) { override fun setHighlight( highlightRange: TextRange, @@ -36,7 +36,11 @@ class ValidationMethodSelectStrategyParamResult( val project = identify.project holder.registerProblem( identify, - MessageBundle.message("inspection.invalid.dao.select.param.strategy.require.type", selectTypeName, requireClassName), + MessageBundle.message( + "inspection.invalid.dao.select.param.strategy.require.type", + genericTypeName, + parentParamTypeName, + ), problemHighlightType(project, shortName), highlightRange, ) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodSelectStrategyReturnTypeResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodSelectStrategyReturnTypeResult.kt new file mode 100644 index 00000000..c3cecf1e --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/validation/result/ValidationMethodSelectStrategyReturnTypeResult.kt @@ -0,0 +1,46 @@ +/* + * Copyright Doma Tools Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.domaframework.doma.intellij.common.validation.result + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import org.domaframework.doma.intellij.bundle.MessageBundle +import org.domaframework.doma.intellij.common.psi.PsiParentClass + +class ValidationMethodSelectStrategyReturnTypeResult( + override val identify: PsiElement?, + override val shortName: String = "", + private val resultType: String, +) : ValidationResult(identify, null, shortName) { + override fun setHighlight( + highlightRange: TextRange, + identify: PsiElement, + holder: ProblemsHolder, + parent: PsiParentClass?, + ) { + val project = identify.project + holder.registerProblem( + identify, + MessageBundle.message( + "inspection.invalid.dao.select.returnType.strategy", + resultType, + ), + problemHighlightType(project, shortName), + highlightRange, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionInOutParamAnnotationTypeChecker.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionInOutParamAnnotationTypeChecker.kt index a332dea5..7bb8cb7c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionInOutParamAnnotationTypeChecker.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionInOutParamAnnotationTypeChecker.kt @@ -22,8 +22,8 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.util.DomaClassName +import org.domaframework.doma.intellij.common.validation.result.ValidationMethodParamsSupportGenericParamResult import org.domaframework.doma.intellij.common.validation.result.ValidationMethodProcedureParamTypeResult -import org.domaframework.doma.intellij.common.validation.result.ValidationMethodProcedureParamsSupportGenericParamResult class ProcedureFunctionInOutParamAnnotationTypeChecker( private val annotationType: ProcedureFunctionParamAnnotationType, @@ -49,7 +49,7 @@ class ProcedureFunctionInOutParamAnnotationTypeChecker( // Check if the parameter type is a valid reference type with generic parameters val referenceParamType = (paramType as? PsiClassType)?.parameters?.firstOrNull() if (referenceParamType == null) { - ValidationMethodProcedureParamsSupportGenericParamResult( + ValidationMethodParamsSupportGenericParamResult( identifier, shortName, "Unknown", @@ -59,7 +59,7 @@ class ProcedureFunctionInOutParamAnnotationTypeChecker( } if (checkParamType(referenceParamType)) return - ValidationMethodProcedureParamsSupportGenericParamResult( + ValidationMethodParamsSupportGenericParamResult( identifier, shortName, referenceParamType.canonicalText, diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionResultSetParamAnnotationTypeChecker.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionResultSetParamAnnotationTypeChecker.kt index 4f706528..26031293 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionResultSetParamAnnotationTypeChecker.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/cheker/ProcedureFunctionResultSetParamAnnotationTypeChecker.kt @@ -23,8 +23,8 @@ import com.intellij.psi.PsiType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.psi.PsiTypeChecker import org.domaframework.doma.intellij.common.util.DomaClassName +import org.domaframework.doma.intellij.common.validation.result.ValidationMethodParamsSupportGenericParamResult import org.domaframework.doma.intellij.common.validation.result.ValidationMethodProcedureParamTypeResult -import org.domaframework.doma.intellij.common.validation.result.ValidationMethodProcedureParamsSupportGenericParamResult import org.domaframework.doma.intellij.extension.getJavaClazz import org.domaframework.doma.intellij.extension.psi.isDomain import org.domaframework.doma.intellij.extension.psi.isEntity @@ -77,7 +77,7 @@ class ProcedureFunctionResultSetParamAnnotationTypeChecker( // Check if the parameter type is a valid List type with generic parameters val listParamType = (paramType as? PsiClassType)?.parameters?.firstOrNull() if (listParamType == null) { - ValidationMethodProcedureParamsSupportGenericParamResult( + ValidationMethodParamsSupportGenericParamResult( identifier, shortName, "Unknown", @@ -88,14 +88,14 @@ class ProcedureFunctionResultSetParamAnnotationTypeChecker( val listCanonicalText = listParamType.canonicalText val result = - ValidationMethodProcedureParamsSupportGenericParamResult( + ValidationMethodParamsSupportGenericParamResult( identifier, shortName, listCanonicalText, annotationType.requireType, ) if (DomaClassName.MAP.isTargetClassNameStartsWith(listCanonicalText)) { - if (checkMapType(listCanonicalText)) result.highlightElement(holder) + if (!checkMapType(listCanonicalText)) result.highlightElement(holder) return } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/paramtype/SelectParamTypeCheckProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/paramtype/SelectParamTypeCheckProcessor.kt index 45f9c574..c906eb41 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/paramtype/SelectParamTypeCheckProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/paramtype/SelectParamTypeCheckProcessor.kt @@ -17,34 +17,22 @@ package org.domaframework.doma.intellij.inspection.dao.processor.paramtype import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiClassType -import com.intellij.psi.PsiParameter import com.intellij.psi.PsiType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.psi.PsiTypeChecker import org.domaframework.doma.intellij.common.util.DomaClassName import org.domaframework.doma.intellij.common.validation.result.ValidationMethodNotSelectStreamParamResult -import org.domaframework.doma.intellij.common.validation.result.ValidationMethodProcedureParamsSupportGenericParamResult +import org.domaframework.doma.intellij.common.validation.result.ValidationMethodParamsSupportGenericParamResult import org.domaframework.doma.intellij.common.validation.result.ValidationMethodSelectStrategyParamResult import org.domaframework.doma.intellij.extension.getJavaClazz -import org.domaframework.doma.intellij.extension.psi.getSuperType import org.domaframework.doma.intellij.extension.psi.isDomain import org.domaframework.doma.intellij.extension.psi.isEntity +import org.domaframework.doma.intellij.inspection.dao.processor.StrategyParam class SelectParamTypeCheckProcessor( private val psiDaoMethod: PsiDaoMethod, private val shortName: String, ) : ParamTypeCheckProcessor(psiDaoMethod, shortName) { - private class StrategyParam( - val fieldName: String = "", - parentClassName: String?, - ) { - private val isSelectType: Boolean = parentClassName == DomaClassName.SELECT_TYPE.className - - fun isStream(): Boolean = fieldName == "STREAM" && isSelectType - - fun isCollect(): Boolean = fieldName == "COLLECT" && isSelectType - } - override fun checkParamType(paramType: PsiType): Boolean { if (PsiTypeChecker.isBaseClassType(paramType)) return true @@ -125,7 +113,7 @@ class SelectParamTypeCheckProcessor( val streamTargetTypeCanonicalText = streamTargetParam.canonicalText if (DomaClassName.MAP.isTargetClassNameStartsWith(streamTargetTypeCanonicalText)) { - if (checkMapType(streamTargetTypeCanonicalText)) { + if (!checkMapType(streamTargetTypeCanonicalText)) { generateTargetTypeResult(streamTargetTypeCanonicalText).highlightElement(holder) } return @@ -158,7 +146,7 @@ class SelectParamTypeCheckProcessor( val streamTargetTypeCanonicalText = collectorTargetParam.canonicalText if (DomaClassName.MAP.isTargetClassNameStartsWith(streamTargetTypeCanonicalText)) { - if (checkMapType(streamTargetTypeCanonicalText)) { + if (!checkMapType(streamTargetTypeCanonicalText)) { generateTargetTypeResult(streamTargetTypeCanonicalText).highlightElement(holder) } return @@ -179,13 +167,8 @@ class SelectParamTypeCheckProcessor( } } - private fun getMethodParamTargetType(typeName: String): PsiParameter? = - method.parameterList.parameters.find { param -> - (param.type as? PsiClassType)?.getSuperType(typeName) != null - } - - fun generateTargetTypeResult(streamParamTypeName: String): ValidationMethodProcedureParamsSupportGenericParamResult = - ValidationMethodProcedureParamsSupportGenericParamResult( + fun generateTargetTypeResult(streamParamTypeName: String): ValidationMethodParamsSupportGenericParamResult = + ValidationMethodParamsSupportGenericParamResult( method.nameIdentifier, shortName, streamParamTypeName, diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/ReturnTypeCheckerProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/ReturnTypeCheckerProcessor.kt index c1b3aa91..d9a9065d 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/ReturnTypeCheckerProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/returntype/ReturnTypeCheckerProcessor.kt @@ -26,20 +26,17 @@ import org.domaframework.doma.intellij.common.validation.result.ValidationReturn import org.domaframework.doma.intellij.extension.getJavaClazz import org.domaframework.doma.intellij.extension.psi.DomaAnnotationType import org.domaframework.doma.intellij.extension.psi.getClassAnnotation +import org.domaframework.doma.intellij.inspection.dao.processor.TypeCheckerProcessor abstract class ReturnTypeCheckerProcessor( private val psiDaoMethod: PsiDaoMethod, private val shortName: String, -) { +) : TypeCheckerProcessor(psiDaoMethod) { protected val returningFqn = DomaClassName.RETURNING.className - protected val method = psiDaoMethod.psiMethod - protected val project = method.project protected val returnType = psiDaoMethod.psiMethod.returnType abstract fun checkReturnType(): ValidationResult? - protected fun getAnnotation(fqName: String): PsiAnnotation = method.annotations.first { it.qualifiedName == fqName } - protected fun isImmutableEntity(canonicalText: String): Boolean { val returnTypeClass = method.project.getJavaClazz(canonicalText) val entity = returnTypeClass?.getClassAnnotation(DomaClassName.ENTITY.className) ?: return false @@ -50,24 +47,12 @@ abstract class ReturnTypeCheckerProcessor( } protected fun hasReturingOption(): Boolean { - val methodAnnotation: PsiAnnotation = getAnnotation(psiDaoMethod.daoType.fqdn) + val methodAnnotation: PsiAnnotation = + getAnnotation(psiDaoMethod.daoType.fqdn) ?: return false val returningOption: PsiAnnotation? = getDaoAnnotationOption(methodAnnotation, "returning") return returningOption?.nameReferenceElement?.qualifiedName == returningFqn } - private fun getDaoAnnotationOption( - psiAnnotation: PsiAnnotation, - findOptionName: String, - ): PsiAnnotation? { - val returningOption = - psiAnnotation.parameterList.attributes - .firstOrNull { param -> - param.name == findOptionName - }?.value as? PsiAnnotation - - return returningOption - } - protected fun generatePsiTypeReturnTypeResult(methodOtherReturnType: PsiType): ValidationResult? = if (returnType != methodOtherReturnType) { ValidationReturnTypeResult( From 3649cfb91ea939428448b3da9702389ffc55d7cc Mon Sep 17 00:00:00 2001 From: xterao Date: Mon, 23 Jun 2025 15:37:15 +0900 Subject: [PATCH 4/4] Add support for Function subtype and Collector subtype in DAO method inspections --- .../SelectParamTypeCheckProcessor.kt | 82 +++++++++++++------ .../AnnotationParamTypeCheckInspectionTest.kt | 2 + ...AnnotationReturnTypeCheckInspectionTest.kt | 1 + .../paramtype/SelectParamTestDao.java | 21 ++++- .../returntype/SelectReturnTypeTestDao.java | 10 +++ .../doma/example/function/HogeFunction.java | 12 +++ 6 files changed, 104 insertions(+), 24 deletions(-) create mode 100644 src/test/testData/src/main/java/doma/example/function/HogeFunction.java diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/paramtype/SelectParamTypeCheckProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/paramtype/SelectParamTypeCheckProcessor.kt index c906eb41..805b5cd8 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/paramtype/SelectParamTypeCheckProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/processor/paramtype/SelectParamTypeCheckProcessor.kt @@ -17,6 +17,7 @@ package org.domaframework.doma.intellij.inspection.dao.processor.paramtype import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiElement import com.intellij.psi.PsiType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.psi.PsiTypeChecker @@ -83,20 +84,36 @@ class SelectParamTypeCheckProcessor( } private fun checkStream(holder: ProblemsHolder) { + val stream = DomaClassName.JAVA_STREAM.className val result = ValidationMethodSelectStrategyParamResult( method.nameIdentifier, shortName, "STREAM", - DomaClassName.JAVA_FUNCTION.getGenericParamCanonicalText(DomaClassName.JAVA_STREAM.className), + DomaClassName.JAVA_FUNCTION.className, ) + val function = getMethodParamTargetType(DomaClassName.JAVA_FUNCTION.className) if (function == null) { result.highlightElement(holder) return } - val functionFirstParam = (function.type as? PsiClassType)?.parameters?.firstOrNull() + val functionType = function.type + val identifier = function.nameIdentifier ?: return + + // Check if the first parameter of the function is a stream type + val functionClass = project.getJavaClazz(functionType.canonicalText) ?: return + var superCollection: PsiClassType? = functionType as PsiClassType? + while (superCollection != null && + !DomaClassName.JAVA_FUNCTION.isTargetClassNameStartsWith(superCollection.canonicalText) + ) { + superCollection = + functionClass.superTypes + .find { sp -> DomaClassName.JAVA_FUNCTION.isTargetClassNameStartsWith(sp.canonicalText) } + } + + val functionFirstParam = superCollection?.parameters?.firstOrNull() if (functionFirstParam == null || !DomaClassName.JAVA_STREAM.isTargetClassNameStartsWith(functionFirstParam.canonicalText) ) { @@ -104,23 +121,24 @@ class SelectParamTypeCheckProcessor( return } - // Check if the first parameter of the function is a stream type - val streamTargetParam = (functionFirstParam as? PsiClassType)?.parameters?.firstOrNull() - if (streamTargetParam == null) { - generateTargetTypeResult("Unknown").highlightElement(holder) + // Check if the first parameter of the stream is a valid type + val streamParamClassType = functionFirstParam as? PsiClassType + val strategyParamType = streamParamClassType?.parameters?.firstOrNull() + if (strategyParamType == null) { + generateTargetTypeResult(identifier, "Unknown", stream).highlightElement(holder) return } - val streamTargetTypeCanonicalText = streamTargetParam.canonicalText - if (DomaClassName.MAP.isTargetClassNameStartsWith(streamTargetTypeCanonicalText)) { - if (!checkMapType(streamTargetTypeCanonicalText)) { - generateTargetTypeResult(streamTargetTypeCanonicalText).highlightElement(holder) + val strategyParamTypeName = strategyParamType.canonicalText + if (DomaClassName.MAP.isTargetClassNameStartsWith(strategyParamTypeName)) { + if (!checkMapType(strategyParamTypeName)) { + generateTargetTypeResult(identifier, strategyParamTypeName, stream).highlightElement(holder) } return } - if (!checkParamType(streamTargetParam)) { - generateTargetTypeResult(streamTargetTypeCanonicalText).highlightElement(holder) + if (!checkParamType(strategyParamType)) { + generateTargetTypeResult(identifier, strategyParamTypeName, stream).highlightElement(holder) } } @@ -132,28 +150,42 @@ class SelectParamTypeCheckProcessor( "COLLECT", DomaClassName.JAVA_COLLECTOR.className, ) + val collector = DomaClassName.JAVA_COLLECTOR.className val collection = getMethodParamTargetType(DomaClassName.JAVA_COLLECTOR.className) if (collection == null) { result.highlightElement(holder) return } - val collectorTargetParam = (collection.type as? PsiClassType)?.parameters?.firstOrNull() + val collectionType = collection.type + val identifier = collection.nameIdentifier ?: return + + val collectionClass = project.getJavaClazz(collectionType.canonicalText) ?: return + var superCollection: PsiClassType? = collection.type as? PsiClassType + while (superCollection != null && + !DomaClassName.JAVA_COLLECTOR.isTargetClassNameStartsWith(superCollection.canonicalText) + ) { + superCollection = + collectionClass.superTypes + .find { sp -> DomaClassName.JAVA_COLLECTOR.isTargetClassNameStartsWith(sp.canonicalText) } + } + + val collectorTargetParam = superCollection?.parameters?.firstOrNull() if (collectorTargetParam == null) { - generateTargetTypeResult("Unknown").highlightElement(holder) + generateTargetTypeResult(identifier, "Unknown", collector).highlightElement(holder) return } - val streamTargetTypeCanonicalText = collectorTargetParam.canonicalText - if (DomaClassName.MAP.isTargetClassNameStartsWith(streamTargetTypeCanonicalText)) { - if (!checkMapType(streamTargetTypeCanonicalText)) { - generateTargetTypeResult(streamTargetTypeCanonicalText).highlightElement(holder) + val collectorTargetTypeCanonicalText = collectorTargetParam.canonicalText + if (DomaClassName.MAP.isTargetClassNameStartsWith(collectorTargetTypeCanonicalText)) { + if (!checkMapType(collectorTargetTypeCanonicalText)) { + generateTargetTypeResult(identifier, collectorTargetTypeCanonicalText, collector).highlightElement(holder) } return } if (!checkParamType(collectorTargetParam)) { - generateTargetTypeResult(streamTargetTypeCanonicalText).highlightElement(holder) + generateTargetTypeResult(identifier, collectorTargetTypeCanonicalText, collector).highlightElement(holder) } } @@ -167,11 +199,15 @@ class SelectParamTypeCheckProcessor( } } - fun generateTargetTypeResult(streamParamTypeName: String): ValidationMethodParamsSupportGenericParamResult = + fun generateTargetTypeResult( + target: PsiElement, + paramTypeName: String, + genericType: String, + ): ValidationMethodParamsSupportGenericParamResult = ValidationMethodParamsSupportGenericParamResult( - method.nameIdentifier, + target, shortName, - streamParamTypeName, - DomaClassName.JAVA_STREAM.className, + paramTypeName, + genericType, ) } diff --git a/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationParamTypeCheckInspectionTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationParamTypeCheckInspectionTest.kt index 44da2ed7..6292e4c2 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationParamTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationParamTypeCheckInspectionTest.kt @@ -39,6 +39,8 @@ class AnnotationParamTypeCheckInspectionTest : DomaSqlTest() { myFixture.enableInspections(DaoMethodParamTypeInspection()) addEntityJavaFile("Pckt.java") addEntityJavaFile("Packet.java") + addOtherJavaFile("collector", "HogeCollector.java") + addOtherJavaFile("function", "HogeFunction.java") testDaoNames.forEach { daoName -> addDaoJavaFile("$daoPackage/$daoName.java") } diff --git a/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationReturnTypeCheckInspectionTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationReturnTypeCheckInspectionTest.kt index 71d5ad6c..e96cb1c2 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationReturnTypeCheckInspectionTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/AnnotationReturnTypeCheckInspectionTest.kt @@ -43,6 +43,7 @@ class AnnotationReturnTypeCheckInspectionTest : DomaSqlTest() { addEntityJavaFile("Pckt.java") addOtherJavaFile("domain", "Hiredate.java") addOtherJavaFile("collector", "HogeCollector.java") + addOtherJavaFile("function", "HogeFunction.java") myFixture.enableInspections(DaoMethodReturnTypeInspection()) } diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/paramtype/SelectParamTestDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/paramtype/SelectParamTestDao.java index 48c77ba3..51f4fd51 100644 --- a/src/test/testData/src/main/java/doma/example/dao/inspection/paramtype/SelectParamTestDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/paramtype/SelectParamTestDao.java @@ -1,8 +1,11 @@ package doma.example.dao.inspection.paramtype; +import java.util.Map; import org.seasar.doma.*; import org.seasar.doma.message.Message; import doma.example.entity.*; +import doma.example.collector.*; +import doma.example.function.*; import java.math.BigDecimal; import java.util.Optional; @@ -22,12 +25,16 @@ public interface SelectParamTestDao { @Select(strategy = SelectType.STREAM) @Sql("Select 10000 from user where name = /* name */'name' and salary = /* salary */0") - Stream \" parameter is required for the method">selectReturnStreamWithStreamOption(String name, BigDecimal salary); + Stream selectReturnStreamWithStreamOption(String name, BigDecimal salary); @Select @Sql("Select 10000 from user") Stream selectReturnStreamWithOutStreamOption(Function, BigDecimal> streams); + @Select(strategy = SelectType.STREAM) + @Sql("Select 10000 from user") + BigDecimal selectFunctionInvalidParam(Function>,BigDecimal> \" is illegal as the type argument of \"java.util.stream.Stream\"">function, BigDecimal stream); + @Select(strategy = SelectType.STREAM) @Sql("Select 10000 from user") Integer selectReturnStream(Function, BigDecimal> stream); @@ -49,6 +56,10 @@ public interface SelectParamTestDao { @Sql("select * from packet where salary > /* salary */0") Pckt selectWithOutCollector(BigDecimal salary, Packet packet); + @Select(strategy = SelectType.COLLECT) + @Sql("select * from packet where salary > /* salary */0") + Pckt selectCollectorInvalidParam(BigDecimal salary, Collector> collector); + @Select(strategy = SelectType.COLLECT) @Sql("select * from emp where salary > /* salary */0") Optional selectCollectOptionalParamResult(BigDecimal salary,Collector> collector); @@ -56,5 +67,13 @@ public interface SelectParamTestDao { @Select(strategy = SelectType.COLLECT) @Sql("select * from emp where salary > /* salary */0") Pckt selectCollectAccumulation(BigDecimal salary, Collector collector); + + @Select(strategy = SelectType.COLLECT) + @Sql("select * from emp where salary > /* salary */0") + Pckt selectHogeCollect(BigDecimal salary, HogeCollector collector); + + @Select(strategy = SelectType.STREAM) + @Sql("select * from emp where salary > /* salary */0") + String selectHogeCollect(BigDecimal salary, HogeFunction function); } diff --git a/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SelectReturnTypeTestDao.java b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SelectReturnTypeTestDao.java index dfb8326f..46ffa3c7 100644 --- a/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SelectReturnTypeTestDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/inspection/returntype/SelectReturnTypeTestDao.java @@ -13,6 +13,7 @@ import doma.example.entity.*; import doma.example.domain.*; import doma.example.collector.*; +import doma.example.function.*; @Dao public interface SelectReturnTypeTestDao { @@ -126,5 +127,14 @@ public interface SelectReturnTypeTestDao { @Select(strategy = SelectType.COLLECT, mapKeyNaming = MapKeyNamingType.CAMEL_CASE) R selectByIdAsMap(Integer id, Collector, ?, R> collector); + @Select(strategy = SelectType.COLLECT) + @Sql("select * from emp where salary > /* salary */0") + Pckt selectHogeCollect(BigDecimal salary, HogeCollector collector); + + @Select(strategy = SelectType.STREAM) + @Sql("select * from emp where salary > /* salary */0") + String selectHogeCollect(BigDecimal salary, HogeFunction function); + + } diff --git a/src/test/testData/src/main/java/doma/example/function/HogeFunction.java b/src/test/testData/src/main/java/doma/example/function/HogeFunction.java new file mode 100644 index 00000000..b7a0d4fd --- /dev/null +++ b/src/test/testData/src/main/java/doma/example/function/HogeFunction.java @@ -0,0 +1,12 @@ +package doma.example.function; + +import java.util.function.Function; +import java.util.stream.Stream; + +public class HogeFunction implements Function, String> { + + @Override + public String apply(Stream t) { + return null; + } +} \ No newline at end of file