From 70990bb2e6bf1178899396f8447919dd1993b5b9 Mon Sep 17 00:00:00 2001 From: xterao Date: Tue, 13 May 2025 19:24:36 +0900 Subject: [PATCH 1/8] Add validation results for directive item types and enhance for directive handling --- .../ValidationForDirectiveItemTypeResult.kt | 44 +++++++++++++++++ .../ValidationNotFoundStaticPropertyResult.kt | 47 +++++++++++++++++++ .../result/ValidationNotFoundTopTypeResult.kt | 44 +++++++++++++++++ .../intellij/common/util/ForDirectiveUtil.kt | 4 ++ .../sql/visitor/SqlInspectionVisitor.kt | 32 ++++++++++--- .../messages/DomaToolsBundle.properties | 5 +- .../messages/DomaToolsBundle_ja.properties | 7 ++- 7 files changed, 173 insertions(+), 10 deletions(-) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationForDirectiveItemTypeResult.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundStaticPropertyResult.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundTopTypeResult.kt diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationForDirectiveItemTypeResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationForDirectiveItemTypeResult.kt new file mode 100644 index 00000000..827b0a0b --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationForDirectiveItemTypeResult.kt @@ -0,0 +1,44 @@ +/* + * 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.sql.validator.result + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +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 ValidationForDirectiveItemTypeResult( + override val identify: PsiElement?, + override val shortName: String = "", +) : ValidationResult(identify, null, shortName) { + override fun setHighlight( + highlightRange: TextRange, + identify: PsiElement, + holder: ProblemsHolder, + parent: PsiParentClass?, + project: Project, + ) { + val project = identify.project + holder.registerProblem( + identify, + MessageBundle.message("inspection.invalid.sql.iterable"), + problemHighlightType(project, shortName), + highlightRange, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundStaticPropertyResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundStaticPropertyResult.kt new file mode 100644 index 00000000..a4cf1876 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundStaticPropertyResult.kt @@ -0,0 +1,47 @@ +/* + * 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.sql.validator.result + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +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 ValidationNotFoundStaticPropertyResult( + override val identify: PsiElement?, + override val shortName: String = "", +) : ValidationResult(identify, null, shortName) { + override fun setHighlight( + highlightRange: TextRange, + identify: PsiElement, + holder: ProblemsHolder, + parent: PsiParentClass?, + project: Project, + ) { + val project = identify.project + holder.registerProblem( + identify, + MessageBundle.message( + "inspection.invalid.sql.staticProperty", + identify.text, + ), + problemHighlightType(project, shortName), + highlightRange, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundTopTypeResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundTopTypeResult.kt new file mode 100644 index 00000000..550bf481 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundTopTypeResult.kt @@ -0,0 +1,44 @@ +/* + * 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.sql.validator.result + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +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 ValidationNotFoundTopTypeResult( + override val identify: PsiElement?, + override val shortName: String = "", +) : ValidationResult(identify, null, shortName) { + override fun setHighlight( + highlightRange: TextRange, + identify: PsiElement, + holder: ProblemsHolder, + parent: PsiParentClass?, + project: Project, + ) { + val project = identify.project + holder.registerProblem( + identify, + MessageBundle.message("inspection.invalid.sql.topType"), + problemHighlightType(project, shortName), + highlightRange, + ) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt index c73fab47..7e92d949 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt @@ -173,6 +173,7 @@ class ForDirectiveUtil { fun getForDirectiveItemClassType( project: Project, forDirectiveBlocks: List, + targetForItem: PsiElement? = null, ): PsiParentClass? { // Get the type of the top for directive definition element // Defined in Dao parameters or static property calls @@ -185,6 +186,9 @@ class ForDirectiveUtil { forDirectiveBlocks.drop(1).forEach { directive -> // Get the definition type of the target directive val formItem = ForItem(directive.item) + if (targetForItem != null && formItem.element.textOffset > targetForItem.textOffset) { + return parentClassType + } val forDirectiveExpr = formItem.getParentForDirectiveExpr() val forDirectiveDeclaration = forDirectiveExpr?.getForItemDeclaration() if (forDirectiveDeclaration != null) { diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt index e6d414aa..63dff771 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt @@ -31,6 +31,7 @@ import org.domaframework.doma.intellij.common.psi.PsiStaticElement import org.domaframework.doma.intellij.common.sql.cleanString import org.domaframework.doma.intellij.common.sql.validator.result.ValidationClassPathResult import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationForDirectiveItemTypeResult import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult import org.domaframework.doma.intellij.common.util.ForDirectiveUtil import org.domaframework.doma.intellij.extension.expr.accessElements @@ -92,10 +93,25 @@ class SqlInspectionVisitor( PsiTreeUtil.getParentOfType(element, SqlElStaticFieldAccessExpr::class.java)?.let { return } val forDirectiveExp = PsiTreeUtil.getParentOfType(element, SqlElForDirective::class.java) - if (forDirectiveExp != null && forDirectiveExp.getForItem() == element) return - - val forItem = ForDirectiveUtil.findForItem(element) - if (forItem != null) return + val isSkip = forDirectiveExp != null && forDirectiveExp.getForItem() != element + + var forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(element, skipSelf = isSkip) + val forItem = ForDirectiveUtil.findForItem(element, skipSelf = isSkip, forDirectives = forDirectiveBlocks) + if (forItem != null) { + val forDeclarationType = + ForDirectiveUtil.getForDirectiveItemClassType( + element.project, + forDirectiveBlocks, + forItem, + ) + if (forDeclarationType == null) { + ValidationForDirectiveItemTypeResult( + element, + this.shortName, + ).highlightElement(holder) + } + return + } val daoMethod = findDaoMethod(visitFile) ?: return val param = daoMethod.findParameter(cleanString(element.text)) @@ -130,10 +146,12 @@ class SqlInspectionVisitor( var isBatchAnnotation = false val topElementParentClass = if (forItem != null) { - val result = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks) + val result = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks, forItem) if (result == null) { - // TODO Add an error message when the type of element used in the For directory is not a List type. - errorHighlight(topElement, daoMethod, holder) + ValidationForDirectiveItemTypeResult( + topElement, + this.shortName, + ).highlightElement(holder) return } val specifiedClassType = diff --git a/src/main/resources/messages/DomaToolsBundle.properties b/src/main/resources/messages/DomaToolsBundle.properties index 51c200ea..a0143e04 100644 --- a/src/main/resources/messages/DomaToolsBundle.properties +++ b/src/main/resources/messages/DomaToolsBundle.properties @@ -6,5 +6,8 @@ inspector.invalid.class.property=The field or method [{1}] does not exist in the inspection.dao.method.variable.error=There are unused parameters in the SQL [{0}] inspector.invalid.dao.parameter=The bind variable [{1}] does not exist in the Dao method [{0}] config.enable.sql.format=Enable SQL Format +inspection.invalid.sql.topType=Can't get type of first element +inspection.invalid.sql.staticProperty=[{0}] is not a public or static property inspection.invalid.sql.testdata=Bind variables must be followed by test data -inspection.invalid.sql.classpath=A non-existent package or class name was specified. [{0}] \ No newline at end of file +inspection.invalid.sql.classpath=A non-existent package or class name was specified [{0}] +inspection.invalid.sql.iterable=The type that can be used in the for directive is an Iterable type \ No newline at end of file diff --git a/src/main/resources/messages/DomaToolsBundle_ja.properties b/src/main/resources/messages/DomaToolsBundle_ja.properties index 269d3216..6d455ff4 100644 --- a/src/main/resources/messages/DomaToolsBundle_ja.properties +++ b/src/main/resources/messages/DomaToolsBundle_ja.properties @@ -3,8 +3,11 @@ jump.to.dao.tooltip.title=Dao\u30E1\u30BD\u30C3\u30C9\u5B9A\u7FA9\u306B\u9077\u7 generate.sql.quickfix.title=SQL\u30D5\u30A1\u30A4\u30EB\u3092\u4F5C\u6210 inspection.sql.not.exist.error=SQL\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u305B\u3093 inspector.invalid.class.property=\u30AF\u30E9\u30B9[{0}]\u306B\u5B58\u5728\u3057\u306A\u3044\u30D5\u30A3\u30FC\u30EB\u30C9\u3001\u307E\u305F\u306F\u30E1\u30BD\u30C3\u30C9\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059:[{1}] -inspection.dao.method.variable.error=SQL\u3067\u4F7F\u7528\u3055\u308C\u3066\u3044\u306A\u3044\u5F15\u6570\u304C\u3042\u308A\u307E\u3059[{0}] +inspection.dao.method.variable.error=SQL\u3067\u4F7F\u7528\u3055\u308C\u3066\u3044\u306A\u3044\u5F15\u6570\u304C\u3042\u308A\u307E\u3059:[{0}] inspector.invalid.dao.parameter=Dao\u30E1\u30BD\u30C3\u30C9[{0}]\u306B\u5B58\u5728\u3057\u306A\u3044\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059:[{1}] config.enable.sql.format=SQL\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u3092\u6709\u52B9\u5316 +inspection.invalid.sql.topType=\u6700\u521D\u306E\u8981\u7D20\u306E\u578B\u304C\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093 +inspection.invalid.sql.staticProperty=[{0}]\u306F\u0070\u0075\u0062\u006C\u0069\u0063\u304B\u3064\u0073\u0074\u0061\u0074\u0069\u0063\u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093 inspection.invalid.sql.testdata=\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u306E\u5F8C\u308D\u306B\u306F\u30C6\u30B9\u30C8\u30C7\u30FC\u30BF\u304C\u5FC5\u8981\u3067\u3059 -inspection.invalid.sql.classpath=\u0041\u0020\u006E\u006F\u006E\u002D\u0065\u0078\u0069\u0073\u0074\u0065\u006E\u0074\u0020\u0070\u0061\u0063\u006B\u0061\u0067\u0065\u0020\u006F\u0072\u0020\u0063\u006C\u0061\u0073\u0073\u0020\u006E\u0061\u006D\u0065\u0020\u0077\u0061\u0073\u0020\u0073\u0070\u0065\u0063\u0069\u0066\u0069\u0065\u0064\u002E [{0}] \ No newline at end of file +inspection.invalid.sql.classpath=\u5B58\u5728\u3057\u306A\u3044\u30D1\u30C3\u30B1\u30FC\u30B8\u307E\u305F\u306F\u30AF\u30E9\u30B9\u540D\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F\u3002:[{0}] +inspection.invalid.sql.iterable=\u0066\u006F\u0072\u30C7\u30A3\u30EC\u30AF\u30C6\u30A3\u30D6\u306B\u4F7F\u7528\u3067\u304D\u308B\u578B\u306F\u0049\u0074\u0065\u0072\u0061\u0062\u006C\u0065\u578B\u3067\u3059 \ No newline at end of file From 482f513ff6c6235435cf7dfad95d3b3de6334b8a Mon Sep 17 00:00:00 2001 From: xterao Date: Tue, 13 May 2025 19:40:58 +0900 Subject: [PATCH 2/8] Fix type resolution for parent elements in nested for directives --- .../doma/intellij/common/util/ForDirectiveUtil.kt | 4 +++- .../sql/provider/SqlParameterCompletionProvider.kt | 4 ++-- .../document/generator/DocumentDaoParameterGenerator.kt | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt index 7e92d949..af8264d4 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt @@ -186,7 +186,9 @@ class ForDirectiveUtil { forDirectiveBlocks.drop(1).forEach { directive -> // Get the definition type of the target directive val formItem = ForItem(directive.item) - if (targetForItem != null && formItem.element.textOffset > targetForItem.textOffset) { + if (targetForItem != null && + formItem.element.textOffset > targetForItem.textOffset + ) { return parentClassType } val forDirectiveExpr = formItem.getParentForDirectiveExpr() diff --git a/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt b/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt index f80a9047..8809cf0f 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt @@ -421,9 +421,9 @@ class SqlParameterCompletionProvider : CompletionProvider( ): Boolean { val project = top.project val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(top) - ForDirectiveUtil.findForItem(top, forDirectives = forDirectiveBlocks) ?: return false + val forItem = ForDirectiveUtil.findForItem(top, forDirectives = forDirectiveBlocks) ?: return false - val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks) ?: return false + val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks, forItem) ?: return false val specifiedClassType = ForDirectiveUtil.resolveForDirectiveItemClassTypeBySuffixElement(top.text) val topClassType = if (specifiedClassType != null) { diff --git a/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt b/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt index 20333ef7..348c28b4 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt @@ -45,8 +45,9 @@ class DocumentDaoParameterGenerator( val searchElement = fieldAccessBlocks?.firstOrNull() ?: originalElement var isBatchAnnotation = false - if (ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives) != null) { - val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives) + val forItem = ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives) + if (forItem != null) { + val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives, forItem) val specifiedClassType = ForDirectiveUtil.resolveForDirectiveItemClassTypeBySuffixElement( searchElement.text, From f51ffd71b9f7082e72c0820ad829a5a7537a80fe Mon Sep 17 00:00:00 2001 From: xterao Date: Wed, 14 May 2025 09:58:12 +0900 Subject: [PATCH 3/8] Add inspection visitor handler --- .../InspectionFieldAccessVisitorProcessor.kt | 100 ++++++++++ .../InspectionPrimaryVisitorProcessor.kt | 79 ++++++++ ...ectionStaticFieldAccessVisitorProcessor.kt | 71 +++++++ .../sql/handler/InspectionVisitorProcessor.kt | 52 +++++ .../sql/visitor/SqlInspectionVisitor.kt | 181 +----------------- .../inspection/sql/visitor/SqlVisitorBase.kt | 14 -- 6 files changed, 311 insertions(+), 186 deletions(-) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionFieldAccessVisitorProcessor.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionPrimaryVisitorProcessor.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionVisitorProcessor.kt diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionFieldAccessVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionFieldAccessVisitorProcessor.kt new file mode 100644 index 00000000..f409c0a5 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionFieldAccessVisitorProcessor.kt @@ -0,0 +1,100 @@ +/* + * 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.sql.handler + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.PsiFile +import org.domaframework.doma.intellij.common.dao.findDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.sql.cleanString +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationForDirectiveItemTypeResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.extension.expr.accessElements +import org.domaframework.doma.intellij.extension.psi.findParameter +import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr +import org.domaframework.doma.intellij.psi.SqlElIdExpr +import org.domaframework.doma.intellij.psi.SqlElPrimaryExpr + +class InspectionFieldAccessVisitorProcessor( + val shortName: String, + private val element: SqlElFieldAccessExpr, +) : InspectionVisitorProcessor(shortName) { + fun check( + holder: ProblemsHolder, + file: PsiFile, + ) { + // Get element inside block comment + val blockElement = getFieldAccessBlocks(element) + val topElm = blockElement.firstOrNull() as SqlElPrimaryExpr + + // Exclude fixed Literal + if (isLiteralOrStatic(topElm)) return + + val topElement = blockElement.firstOrNull() ?: return + val daoMethod = findDaoMethod(file) ?: return + val project = topElement.project + val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(topElement) + val forItem = ForDirectiveUtil.findForItem(topElement, forDirectives = forDirectiveBlocks) + var isBatchAnnotation = false + val topElementParentClass = + if (forItem != null) { + val result = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks, forItem) + if (result == null) { + ValidationForDirectiveItemTypeResult( + topElement, + this.shortName, + ).highlightElement(holder) + return + } + val specifiedClassType = + ForDirectiveUtil.resolveForDirectiveItemClassTypeBySuffixElement(topElement.text) + if (specifiedClassType != null) { + PsiParentClass(specifiedClassType) + } else { + result + } + } else { + val paramType = daoMethod.findParameter(cleanString(topElement.text))?.type + if (paramType == null) { + errorHighlight(topElement, daoMethod, holder) + return + } + isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation() + PsiParentClass(paramType) + } + + val result = + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + blockElement, + project, + topElementParentClass, + shortName = this.shortName, + isBatchAnnotation = isBatchAnnotation, + ) + + result?.highlightElement(holder) + } + + fun getFieldAccessBlocks(element: SqlElFieldAccessExpr): List { + val blockElements = element.accessElements + (blockElements.firstOrNull() as? SqlElPrimaryExpr) + ?.let { if (isLiteralOrStatic(it)) return emptyList() } + ?: return emptyList() + + return blockElements.mapNotNull { it as SqlElIdExpr } + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionPrimaryVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionPrimaryVisitorProcessor.kt new file mode 100644 index 00000000..4bacf900 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionPrimaryVisitorProcessor.kt @@ -0,0 +1,79 @@ +/* + * 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.sql.handler + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil +import org.domaframework.doma.intellij.common.dao.findDaoMethod +import org.domaframework.doma.intellij.common.sql.cleanString +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationForDirectiveItemTypeResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.extension.psi.findParameter +import org.domaframework.doma.intellij.extension.psi.getForItem +import org.domaframework.doma.intellij.psi.SqlElForDirective +import org.domaframework.doma.intellij.psi.SqlElPrimaryExpr +import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr + +class InspectionPrimaryVisitorProcessor( + val shortName: String, + private val element: SqlElPrimaryExpr, +) : InspectionVisitorProcessor(shortName) { + fun check( + holder: ProblemsHolder, + file: PsiFile, + ) { + if (isLiteralOrStatic(element)) return + PsiTreeUtil.getParentOfType(element, SqlElStaticFieldAccessExpr::class.java)?.let { return } + + val forDirectiveExp = PsiTreeUtil.getParentOfType(element, SqlElForDirective::class.java) + val isSkip = forDirectiveExp != null && forDirectiveExp.getForItem() != element + + var forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(element, skipSelf = isSkip) + val forItem = + ForDirectiveUtil.findForItem( + element, + skipSelf = isSkip, + forDirectives = forDirectiveBlocks, + ) + if (forItem != null) { + val forDeclarationType = + ForDirectiveUtil.getForDirectiveItemClassType( + element.project, + forDirectiveBlocks, + forItem, + ) + if (forDeclarationType == null) { + ValidationForDirectiveItemTypeResult( + element, + this.shortName, + ).highlightElement(holder) + } + return + } + + val daoMethod = findDaoMethod(file) ?: return + val param = daoMethod.findParameter(cleanString(element.text)) + if (param != null) return + + ValidationDaoParamResult( + element, + daoMethod.name, + this.shortName, + ).highlightElement(holder) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt new file mode 100644 index 00000000..e29cdd0f --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt @@ -0,0 +1,71 @@ +/* + * 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.sql.handler + +import com.intellij.codeInspection.ProblemsHolder +import org.domaframework.doma.intellij.common.psi.PsiParentClass +import org.domaframework.doma.intellij.common.psi.PsiStaticElement +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationClassPathResult +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.extension.expr.accessElements +import org.domaframework.doma.intellij.extension.psi.psiClassType +import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr + +class InspectionStaticFieldAccessVisitorProcessor( + val shortName: String, +) : InspectionVisitorProcessor(shortName) { + /** + * Check for existence of static field + */ + fun check( + staticAccuser: SqlElStaticFieldAccessExpr, + holder: ProblemsHolder, + ) { + val blockElements = staticAccuser.accessElements + val psiStaticClass = PsiStaticElement(staticAccuser.elClass.elIdExprList, staticAccuser.containingFile) + val referenceClass = psiStaticClass.getRefClazz() + if (referenceClass == null) { + ValidationClassPathResult( + staticAccuser.elClass, + shortName, + ).highlightElement(holder) + return + } + + val topParentClass = ForDirectiveUtil.getStaticFieldAccessTopElementClassType(staticAccuser, referenceClass) + if (topParentClass == null) { + blockElements.firstOrNull()?.let { + ValidationPropertyResult( + it, + PsiParentClass(referenceClass.psiClassType), + shortName, + ).highlightElement(holder) + } + return + } + val result = + topParentClass.let { + ForDirectiveUtil.getFieldAccessLastPropertyClassType( + blockElements, + staticAccuser.project, + it, + shortName = shortName, + ) + } + result?.highlightElement(holder) + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionVisitorProcessor.kt new file mode 100644 index 00000000..a1b235e5 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionVisitorProcessor.kt @@ -0,0 +1,52 @@ +/* + * 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.sql.handler + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.util.elementType +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult +import org.domaframework.doma.intellij.psi.SqlElIdExpr +import org.domaframework.doma.intellij.psi.SqlElNewExpr +import org.domaframework.doma.intellij.psi.SqlTypes + +abstract class InspectionVisitorProcessor( + private val shortName: String, +) { + protected fun errorHighlight( + topElement: SqlElIdExpr, + daoMethod: PsiMethod, + holder: ProblemsHolder, + ) { + ValidationDaoParamResult( + topElement, + daoMethod.name, + this.shortName, + ).highlightElement(holder) + } + + protected fun isLiteralOrStatic(targetElement: PsiElement): Boolean = + ( + targetElement.firstChild?.elementType == SqlTypes.EL_STRING || + targetElement.firstChild?.elementType == SqlTypes.EL_CHAR || + targetElement.firstChild?.elementType == SqlTypes.EL_NUMBER || + targetElement.firstChild?.elementType == SqlTypes.EL_NULL || + targetElement.firstChild?.elementType == SqlTypes.BOOLEAN || + targetElement.firstChild is SqlElNewExpr || + targetElement.text.startsWith("@") + ) +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt index 63dff771..abc7e851 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt @@ -19,29 +19,14 @@ import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiLiteralExpression -import com.intellij.psi.PsiMethod -import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType -import org.domaframework.doma.intellij.common.dao.findDaoMethod import org.domaframework.doma.intellij.common.isInjectionSqlFile import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType -import org.domaframework.doma.intellij.common.psi.PsiDaoMethod -import org.domaframework.doma.intellij.common.psi.PsiParentClass -import org.domaframework.doma.intellij.common.psi.PsiStaticElement -import org.domaframework.doma.intellij.common.sql.cleanString -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationClassPathResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationForDirectiveItemTypeResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult -import org.domaframework.doma.intellij.common.util.ForDirectiveUtil -import org.domaframework.doma.intellij.extension.expr.accessElements -import org.domaframework.doma.intellij.extension.psi.findParameter -import org.domaframework.doma.intellij.extension.psi.getForItem import org.domaframework.doma.intellij.extension.psi.isFirstElement -import org.domaframework.doma.intellij.extension.psi.psiClassType +import org.domaframework.doma.intellij.inspection.sql.handler.InspectionFieldAccessVisitorProcessor +import org.domaframework.doma.intellij.inspection.sql.handler.InspectionPrimaryVisitorProcessor +import org.domaframework.doma.intellij.inspection.sql.handler.InspectionStaticFieldAccessVisitorProcessor import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr -import org.domaframework.doma.intellij.psi.SqlElForDirective -import org.domaframework.doma.intellij.psi.SqlElIdExpr import org.domaframework.doma.intellij.psi.SqlElPrimaryExpr import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlTypes @@ -65,7 +50,8 @@ class SqlInspectionVisitor( override fun visitElStaticFieldAccessExpr(element: SqlElStaticFieldAccessExpr) { super.visitElStaticFieldAccessExpr(element) - checkStaticFieldAndMethodAccess(element, holder) + val handler = InspectionStaticFieldAccessVisitorProcessor(this.shortName) + handler.check(element, holder) } override fun visitElFieldAccessExpr(element: SqlElFieldAccessExpr) { @@ -73,14 +59,8 @@ class SqlInspectionVisitor( if (setFile(element)) return val visitFile: PsiFile = file ?: return - // Get element inside block comment - val blockElement = getFieldAccessBlocks(element) - val topElm = blockElement.firstOrNull() as SqlElPrimaryExpr - - // Exclude fixed Literal - if (isLiteralOrStatic(topElm)) return - - checkAccessFieldAndMethod(holder, blockElement, visitFile) + val handler = InspectionFieldAccessVisitorProcessor(shortName, element) + handler.check(holder, visitFile) } override fun visitElPrimaryExpr(element: SqlElPrimaryExpr) { @@ -89,150 +69,7 @@ class SqlInspectionVisitor( if (setFile(element)) return val visitFile: PsiFile = file ?: return - if (isLiteralOrStatic(element)) return - PsiTreeUtil.getParentOfType(element, SqlElStaticFieldAccessExpr::class.java)?.let { return } - - val forDirectiveExp = PsiTreeUtil.getParentOfType(element, SqlElForDirective::class.java) - val isSkip = forDirectiveExp != null && forDirectiveExp.getForItem() != element - - var forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(element, skipSelf = isSkip) - val forItem = ForDirectiveUtil.findForItem(element, skipSelf = isSkip, forDirectives = forDirectiveBlocks) - if (forItem != null) { - val forDeclarationType = - ForDirectiveUtil.getForDirectiveItemClassType( - element.project, - forDirectiveBlocks, - forItem, - ) - if (forDeclarationType == null) { - ValidationForDirectiveItemTypeResult( - element, - this.shortName, - ).highlightElement(holder) - } - return - } - - val daoMethod = findDaoMethod(visitFile) ?: return - val param = daoMethod.findParameter(cleanString(element.text)) - if (param != null) return - - ValidationDaoParamResult( - element, - daoMethod.name, - this.shortName, - ).highlightElement(holder) - } - - private fun getFieldAccessBlocks(element: SqlElFieldAccessExpr): List { - val blockElements = element.accessElements - (blockElements.firstOrNull() as? SqlElPrimaryExpr) - ?.let { if (isLiteralOrStatic(it)) return emptyList() } - ?: return emptyList() - - return blockElements.mapNotNull { it as SqlElIdExpr } - } - - private fun checkAccessFieldAndMethod( - holder: ProblemsHolder, - blockElement: List, - file: PsiFile, - ) { - val topElement = blockElement.firstOrNull() ?: return - val daoMethod = findDaoMethod(file) ?: return - val project = topElement.project - val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(topElement) - val forItem = ForDirectiveUtil.findForItem(topElement, forDirectives = forDirectiveBlocks) - var isBatchAnnotation = false - val topElementParentClass = - if (forItem != null) { - val result = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks, forItem) - if (result == null) { - ValidationForDirectiveItemTypeResult( - topElement, - this.shortName, - ).highlightElement(holder) - return - } - val specifiedClassType = - ForDirectiveUtil.resolveForDirectiveItemClassTypeBySuffixElement(topElement.text) - if (specifiedClassType != null) { - PsiParentClass(specifiedClassType) - } else { - result - } - } else { - val paramType = daoMethod.findParameter(cleanString(topElement.text))?.type - if (paramType == null) { - errorHighlight(topElement, daoMethod, holder) - return - } - isBatchAnnotation = PsiDaoMethod(project, daoMethod).daoType.isBatchAnnotation() - PsiParentClass(paramType) - } - - val result = - ForDirectiveUtil.getFieldAccessLastPropertyClassType( - blockElement, - project, - topElementParentClass, - shortName = this.shortName, - isBatchAnnotation = isBatchAnnotation, - ) - - result?.highlightElement(holder) - } - - private fun errorHighlight( - topElement: SqlElIdExpr, - daoMethod: PsiMethod, - holder: ProblemsHolder, - ) { - ValidationDaoParamResult( - topElement, - daoMethod.name, - this.shortName, - ).highlightElement(holder) - } - - /** - * Check for existence of static field - */ - private fun checkStaticFieldAndMethodAccess( - staticAccuser: SqlElStaticFieldAccessExpr, - holder: ProblemsHolder, - ) { - val blockElements = staticAccuser.accessElements - val psiStaticClass = PsiStaticElement(staticAccuser.elClass.elIdExprList, staticAccuser.containingFile) - val referenceClass = psiStaticClass.getRefClazz() - if (referenceClass == null) { - ValidationClassPathResult( - staticAccuser.elClass, - this.shortName, - ).highlightElement(holder) - return - } - - val topParentClass = ForDirectiveUtil.getStaticFieldAccessTopElementClassType(staticAccuser, referenceClass) - if (topParentClass == null) { - blockElements.firstOrNull()?.let { - ValidationPropertyResult( - it, - PsiParentClass(referenceClass.psiClassType), - this.shortName, - ).highlightElement(holder) - } - return - } - val result = - topParentClass.let { - ForDirectiveUtil.getFieldAccessLastPropertyClassType( - blockElements, - staticAccuser.project, - it, - shortName = this.shortName, - ) - } - result?.highlightElement(holder) + val handler = InspectionPrimaryVisitorProcessor(this.shortName, element) + handler.check(holder, visitFile) } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlVisitorBase.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlVisitorBase.kt index 67fab189..d228201b 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlVisitorBase.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlVisitorBase.kt @@ -20,10 +20,7 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiLiteralExpression -import com.intellij.psi.util.elementType import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType -import org.domaframework.doma.intellij.psi.SqlElNewExpr -import org.domaframework.doma.intellij.psi.SqlTypes import org.domaframework.doma.intellij.psi.SqlVisitor open class SqlVisitorBase : SqlVisitor() { @@ -56,15 +53,4 @@ open class SqlVisitorBase : SqlVisitor() { false -> null } - - protected fun isLiteralOrStatic(targetElement: PsiElement): Boolean = - ( - targetElement.firstChild?.elementType == SqlTypes.EL_STRING || - targetElement.firstChild?.elementType == SqlTypes.EL_CHAR || - targetElement.firstChild?.elementType == SqlTypes.EL_NUMBER || - targetElement.firstChild?.elementType == SqlTypes.EL_NULL || - targetElement.firstChild?.elementType == SqlTypes.BOOLEAN || - targetElement.firstChild is SqlElNewExpr || - targetElement.text.startsWith("@") - ) } From 538c95733d7c20088f680f0fc3118730d2b0455f Mon Sep 17 00:00:00 2001 From: xterao Date: Wed, 14 May 2025 13:26:46 +0900 Subject: [PATCH 4/8] Update static property validation to include class context in error message --- .../result/ValidationNotFoundStaticPropertyResult.kt | 3 +++ .../InspectionStaticFieldAccessVisitorProcessor.kt | 8 +++----- src/main/resources/messages/DomaToolsBundle.properties | 2 +- src/main/resources/messages/DomaToolsBundle_ja.properties | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundStaticPropertyResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundStaticPropertyResult.kt index a4cf1876..96334044 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundStaticPropertyResult.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationNotFoundStaticPropertyResult.kt @@ -21,9 +21,11 @@ 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 +import org.domaframework.doma.intellij.psi.SqlElClass class ValidationNotFoundStaticPropertyResult( override val identify: PsiElement?, + val clazz: SqlElClass, override val shortName: String = "", ) : ValidationResult(identify, null, shortName) { override fun setHighlight( @@ -39,6 +41,7 @@ class ValidationNotFoundStaticPropertyResult( MessageBundle.message( "inspection.invalid.sql.staticProperty", identify.text, + clazz.text, ), problemHighlightType(project, shortName), highlightRange, diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt index e29cdd0f..2e99a998 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt @@ -16,13 +16,11 @@ package org.domaframework.doma.intellij.inspection.sql.handler import com.intellij.codeInspection.ProblemsHolder -import org.domaframework.doma.intellij.common.psi.PsiParentClass import org.domaframework.doma.intellij.common.psi.PsiStaticElement import org.domaframework.doma.intellij.common.sql.validator.result.ValidationClassPathResult -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationNotFoundStaticPropertyResult import org.domaframework.doma.intellij.common.util.ForDirectiveUtil import org.domaframework.doma.intellij.extension.expr.accessElements -import org.domaframework.doma.intellij.extension.psi.psiClassType import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr class InspectionStaticFieldAccessVisitorProcessor( @@ -49,9 +47,9 @@ class InspectionStaticFieldAccessVisitorProcessor( val topParentClass = ForDirectiveUtil.getStaticFieldAccessTopElementClassType(staticAccuser, referenceClass) if (topParentClass == null) { blockElements.firstOrNull()?.let { - ValidationPropertyResult( + ValidationNotFoundStaticPropertyResult( it, - PsiParentClass(referenceClass.psiClassType), + staticAccuser.elClass, shortName, ).highlightElement(holder) } diff --git a/src/main/resources/messages/DomaToolsBundle.properties b/src/main/resources/messages/DomaToolsBundle.properties index a0143e04..e2f5d1f8 100644 --- a/src/main/resources/messages/DomaToolsBundle.properties +++ b/src/main/resources/messages/DomaToolsBundle.properties @@ -7,7 +7,7 @@ inspection.dao.method.variable.error=There are unused parameters in the SQL [{0} inspector.invalid.dao.parameter=The bind variable [{1}] does not exist in the Dao method [{0}] config.enable.sql.format=Enable SQL Format inspection.invalid.sql.topType=Can't get type of first element -inspection.invalid.sql.staticProperty=[{0}] is not a public or static property +inspection.invalid.sql.staticProperty=[{0}] is not a public or static property in the class [{1}] inspection.invalid.sql.testdata=Bind variables must be followed by test data inspection.invalid.sql.classpath=A non-existent package or class name was specified [{0}] inspection.invalid.sql.iterable=The type that can be used in the for directive is an Iterable type \ No newline at end of file diff --git a/src/main/resources/messages/DomaToolsBundle_ja.properties b/src/main/resources/messages/DomaToolsBundle_ja.properties index 6d455ff4..b33d5de6 100644 --- a/src/main/resources/messages/DomaToolsBundle_ja.properties +++ b/src/main/resources/messages/DomaToolsBundle_ja.properties @@ -7,7 +7,7 @@ inspection.dao.method.variable.error=SQL\u3067\u4F7F\u7528\u3055\u308C\u3066\u30 inspector.invalid.dao.parameter=Dao\u30E1\u30BD\u30C3\u30C9[{0}]\u306B\u5B58\u5728\u3057\u306A\u3044\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059:[{1}] config.enable.sql.format=SQL\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u3092\u6709\u52B9\u5316 inspection.invalid.sql.topType=\u6700\u521D\u306E\u8981\u7D20\u306E\u578B\u304C\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093 -inspection.invalid.sql.staticProperty=[{0}]\u306F\u0070\u0075\u0062\u006C\u0069\u0063\u304B\u3064\u0073\u0074\u0061\u0074\u0069\u0063\u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093 +inspection.invalid.sql.staticProperty=[{0}]\u0020\u306F\u30AF\u30E9\u30B9\u0020[{1}]\u0020\u306E\u0020\u0070\u0075\u0062\u006C\u0069\u0063\u0020\u307E\u305F\u0020\u0073\u0074\u0061\u0074\u0069\u0063\u0020\u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093 inspection.invalid.sql.testdata=\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u306E\u5F8C\u308D\u306B\u306F\u30C6\u30B9\u30C8\u30C7\u30FC\u30BF\u304C\u5FC5\u8981\u3067\u3059 inspection.invalid.sql.classpath=\u5B58\u5728\u3057\u306A\u3044\u30D1\u30C3\u30B1\u30FC\u30B8\u307E\u305F\u306F\u30AF\u30E9\u30B9\u540D\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F\u3002:[{0}] inspection.invalid.sql.iterable=\u0066\u006F\u0072\u30C7\u30A3\u30EC\u30AF\u30C6\u30A3\u30D6\u306B\u4F7F\u7528\u3067\u304D\u308B\u578B\u306F\u0049\u0074\u0065\u0072\u0061\u0062\u006C\u0065\u578B\u3067\u3059 \ No newline at end of file From 895ebd15b89def1fade4b1d47e28dbd2b690b469 Mon Sep 17 00:00:00 2001 From: xterao Date: Wed, 14 May 2025 16:07:06 +0900 Subject: [PATCH 5/8] Check for DAO parameters that overlap with for directive definition names --- .../inspector/DaoMethodVariableInspector.kt | 72 ++++++++++++++----- .../messages/DomaToolsBundle.properties | 3 +- .../messages/DomaToolsBundle_ja.properties | 3 +- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/DaoMethodVariableInspector.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/DaoMethodVariableInspector.kt index 39f3a955..c172824c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/DaoMethodVariableInspector.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/DaoMethodVariableInspector.kt @@ -33,18 +33,26 @@ import org.domaframework.doma.intellij.bundle.MessageBundle import org.domaframework.doma.intellij.common.dao.getDaoClass import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType import org.domaframework.doma.intellij.common.psi.PsiDaoMethod +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil import org.domaframework.doma.intellij.extension.findFile +import org.domaframework.doma.intellij.extension.psi.getForItem import org.domaframework.doma.intellij.extension.psi.isCollector import org.domaframework.doma.intellij.extension.psi.isFunctionClazz import org.domaframework.doma.intellij.extension.psi.isSelectOption import org.domaframework.doma.intellij.extension.psi.methodParameters -import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr +import org.domaframework.doma.intellij.psi.SqlElForDirective +import org.domaframework.doma.intellij.psi.SqlElPrimaryExpr import org.domaframework.doma.intellij.psi.SqlTypes /** * Check if Dao method arguments are used in the corresponding SQL file */ class DaoMethodVariableInspector : AbstractBaseJavaLocalInspectionTool() { + private data class DaoMethodVariableVisitorResult( + val elements: List, + val deplicateForItemElements: List, + ) + override fun getDisplayName(): String = "Method argument usage check" override fun getShortName(): String = "org.domaframework.doma.intellij.variablechecker" @@ -76,38 +84,65 @@ class DaoMethodVariableInspector : AbstractBaseJavaLocalInspectionTool() { method.project.findFile(it) } ?: return - findElementsInSqlFile(sqlFileManager, methodParameters.toList()).forEach { arg -> - holder.registerProblem( - (arg.originalElement as PsiParameterImpl).nameIdentifier, - MessageBundle.message("inspection.dao.method.variable.error", arg.name), - ProblemHighlightType.ERROR, - ) + val params = methodParameters.toList() + val result = findElementsInSqlFile(sqlFileManager, params) + params.forEach { param -> + if (!result.elements.contains(param)) { + val message = + if (result.deplicateForItemElements.contains(param)) { + MessageBundle.message("inspection.invalid.dao.duplicate") + } else { + MessageBundle.message( + "inspection.dao.method.variable.error", + param.name, + ) + } + holder.registerProblem( + (param.originalElement as PsiParameterImpl).nameIdentifier, + message, + ProblemHighlightType.ERROR, + ) + } } } } } - fun findElementsInSqlFile( + private fun findElementsInSqlFile( sqlFile: PsiFile, args: List, - ): List { + ): DaoMethodVariableVisitorResult { val elements = mutableListOf() + val deplicateForItemElements = mutableListOf() var iterator: Iterator sqlFile.accept( object : PsiRecursiveElementVisitor() { // Recursively explore child elements in a file with PsiRecursiveElementVisitor. override fun visitElement(element: PsiElement) { - if (element.elementType == SqlTypes.EL_IDENTIFIER && element.prevSibling?.elementType != SqlTypes.DOT) { + if (( + element.elementType == SqlTypes.EL_IDENTIFIER || + element is SqlElPrimaryExpr + ) && + element.prevSibling?.elementType != SqlTypes.DOT + ) { iterator = args.minus(elements.toSet()).iterator() while (iterator.hasNext()) { val arg = iterator.next() - val fieldAccessExpr = - PsiTreeUtil.getParentOfType( - element, - SqlElStaticFieldAccessExpr::class.java, - ) - if (fieldAccessExpr == null && element.text == arg.name) { - elements.add(arg) + if (element.text == arg.name) { + // Check if you are in a For directive + val elementParent = PsiTreeUtil.getParentOfType(element, SqlElForDirective::class.java) + val isForItemSide = elementParent?.getForItem()?.textOffset == element.textOffset + + // Check if the element name definition source is in the for directive + val forDirectiveBlocks = + ForDirectiveUtil.getForDirectiveBlocks(element) + val forItem = ForDirectiveUtil.findForItem(element, forDirectives = forDirectiveBlocks) + + if (forItem != null || isForItemSide) { + deplicateForItemElements.add(arg) + } else { + elements.add(arg) + } break } } @@ -116,6 +151,7 @@ class DaoMethodVariableInspector : AbstractBaseJavaLocalInspectionTool() { } }, ) - return args.minus(elements.toSet()) + val result = DaoMethodVariableVisitorResult(elements, deplicateForItemElements) + return result } } diff --git a/src/main/resources/messages/DomaToolsBundle.properties b/src/main/resources/messages/DomaToolsBundle.properties index e2f5d1f8..ca5ee82f 100644 --- a/src/main/resources/messages/DomaToolsBundle.properties +++ b/src/main/resources/messages/DomaToolsBundle.properties @@ -10,4 +10,5 @@ inspection.invalid.sql.topType=Can't get type of first element inspection.invalid.sql.staticProperty=[{0}] is not a public or static property in the class [{1}] inspection.invalid.sql.testdata=Bind variables must be followed by test data inspection.invalid.sql.classpath=A non-existent package or class name was specified [{0}] -inspection.invalid.sql.iterable=The type that can be used in the for directive is an Iterable type \ No newline at end of file +inspection.invalid.sql.iterable=The type that can be used in the for directive is an Iterable type +inspection.invalid.dao.duplicate=An element name that is a duplicate of an element name defined in SQL is used \ No newline at end of file diff --git a/src/main/resources/messages/DomaToolsBundle_ja.properties b/src/main/resources/messages/DomaToolsBundle_ja.properties index b33d5de6..dd7b846f 100644 --- a/src/main/resources/messages/DomaToolsBundle_ja.properties +++ b/src/main/resources/messages/DomaToolsBundle_ja.properties @@ -10,4 +10,5 @@ inspection.invalid.sql.topType=\u6700\u521D\u306E\u8981\u7D20\u306E\u578B\u304C\ inspection.invalid.sql.staticProperty=[{0}]\u0020\u306F\u30AF\u30E9\u30B9\u0020[{1}]\u0020\u306E\u0020\u0070\u0075\u0062\u006C\u0069\u0063\u0020\u307E\u305F\u0020\u0073\u0074\u0061\u0074\u0069\u0063\u0020\u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093 inspection.invalid.sql.testdata=\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u306E\u5F8C\u308D\u306B\u306F\u30C6\u30B9\u30C8\u30C7\u30FC\u30BF\u304C\u5FC5\u8981\u3067\u3059 inspection.invalid.sql.classpath=\u5B58\u5728\u3057\u306A\u3044\u30D1\u30C3\u30B1\u30FC\u30B8\u307E\u305F\u306F\u30AF\u30E9\u30B9\u540D\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F\u3002:[{0}] -inspection.invalid.sql.iterable=\u0066\u006F\u0072\u30C7\u30A3\u30EC\u30AF\u30C6\u30A3\u30D6\u306B\u4F7F\u7528\u3067\u304D\u308B\u578B\u306F\u0049\u0074\u0065\u0072\u0061\u0062\u006C\u0065\u578B\u3067\u3059 \ No newline at end of file +inspection.invalid.sql.iterable=\u0066\u006F\u0072\u30C7\u30A3\u30EC\u30AF\u30C6\u30A3\u30D6\u306B\u4F7F\u7528\u3067\u304D\u308B\u578B\u306F\u0049\u0074\u0065\u0072\u0061\u0062\u006C\u0065\u578B\u3067\u3059 +inspection.invalid.dao.duplicate=\u0053\u0051\u004C\u5185\u3067\u5B9A\u7FA9\u3055\u308C\u305F\u8981\u7D20\u540D\u3068\u91CD\u8907\u3057\u305F\u8981\u7D20\u540D\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059 \ No newline at end of file From ea8884bab951a67bfdf172269f9816c7d9fef9de Mon Sep 17 00:00:00 2001 From: xterao Date: Wed, 14 May 2025 17:43:23 +0900 Subject: [PATCH 6/8] What to do when the for directive definition element is used as the top element of a field access --- .../intellij/common/util/ForDirectiveUtil.kt | 88 +++++++++++++------ .../InspectionFieldAccessVisitorProcessor.kt | 9 +- .../InspectionForDirectiveVisitorProcessor.kt | 41 +++++++++ .../InspectionPrimaryVisitorProcessor.kt | 4 +- ...ectionStaticFieldAccessVisitorProcessor.kt | 2 +- .../InspectionVisitorProcessor.kt | 2 +- .../sql/visitor/SqlInspectionVisitor.kt | 26 ++++-- .../reference/SqlElIdExprReference.kt | 35 +++++--- .../accessStaticProperty.sql | 4 +- .../bindVariableForNonEntityClass.sql | 4 +- .../callStaticPropertyPackageName.sql | 6 +- 11 files changed, 158 insertions(+), 63 deletions(-) rename src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/{handler => processor}/InspectionFieldAccessVisitorProcessor.kt (91%) create mode 100644 src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionForDirectiveVisitorProcessor.kt rename src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/{handler => processor}/InspectionPrimaryVisitorProcessor.kt (96%) rename src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/{handler => processor}/InspectionStaticFieldAccessVisitorProcessor.kt (97%) rename src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/{handler => processor}/InspectionVisitorProcessor.kt (96%) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt index af8264d4..c8b1ac0d 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/util/ForDirectiveUtil.kt @@ -19,6 +19,7 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass import com.intellij.psi.PsiClassType import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod import com.intellij.psi.PsiType import com.intellij.psi.PsiTypes import com.intellij.psi.util.CachedValue @@ -34,6 +35,7 @@ import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil import org.domaframework.doma.intellij.common.sql.cleanString import org.domaframework.doma.intellij.common.sql.foritem.ForItem import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationNotFoundTopTypeResult import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult import org.domaframework.doma.intellij.extension.expr.accessElements @@ -178,40 +180,72 @@ class ForDirectiveUtil { // Get the type of the top for directive definition element // Defined in Dao parameters or static property calls if (forDirectiveBlocks.isEmpty()) return null + val topDirectiveItem = forDirectiveBlocks.first().item + val file = topDirectiveItem.containingFile ?: return null + val daoMethod = findDaoMethod(file) + var parentClassType = getTopForDirectiveDeclarationClassType( forDirectiveBlocks.first().item, project, - ) ?: return null + daoMethod, + ) forDirectiveBlocks.drop(1).forEach { directive -> // Get the definition type of the target directive val formItem = ForItem(directive.item) - if (targetForItem != null && - formItem.element.textOffset > targetForItem.textOffset - ) { + if (targetForItem != null && formItem.element.textOffset > targetForItem.textOffset) { return parentClassType } val forDirectiveExpr = formItem.getParentForDirectiveExpr() val forDirectiveDeclaration = forDirectiveExpr?.getForItemDeclaration() if (forDirectiveDeclaration != null) { - val forItemDeclarationBlocks = forDirectiveDeclaration.getDeclarationChildren() - getFieldAccessLastPropertyClassType( - forItemDeclarationBlocks, - project, - parentClassType, - complete = { lastType -> - val classType = lastType.type as? PsiClassType - val nestClass = - if (classType != null && - PsiClassTypeUtil.Companion.isIterableType(classType, project) - ) { - classType.parameters.firstOrNull() - } else { - null - } - nestClass?.let { parentClassType = PsiParentClass(it) } - }, - ) + val declarationTopElement = + forDirectiveDeclaration.getDeclarationChildren().first() + val findDeclarationForItem = + findForItem(declarationTopElement, forDirectives = forDirectiveBlocks) + + if (findDeclarationForItem == null && daoMethod != null) { + val matchParam = daoMethod.findParameter(declarationTopElement.text) + if (matchParam != null) { + val convertOptional = + PsiClassTypeUtil.convertOptionalType(matchParam.type, project) + parentClassType = PsiParentClass(convertOptional) + } + } + if (parentClassType != null) { + val isBatchAnnotation = daoMethod?.let { PsiDaoMethod(project, it).daoType.isBatchAnnotation() } == true + val forItemDeclarationBlocks = + forDirectiveDeclaration.getDeclarationChildren() + getFieldAccessLastPropertyClassType( + forItemDeclarationBlocks, + project, + parentClassType, + isBatchAnnotation = isBatchAnnotation, + complete = { lastType -> + val classType = lastType.type as? PsiClassType + val nestClass = + if (classType != null && + PsiClassTypeUtil.Companion.isIterableType( + classType, + project, + ) + ) { + classType.parameters.firstOrNull() + } else { + null + } + parentClassType = + if (nestClass != null) { + PsiParentClass(nestClass) + } else { + null + } + }, + ) + if (targetForItem != null && formItem.element.text == targetForItem.text) { + return parentClassType + } + } } } @@ -221,6 +255,7 @@ class ForDirectiveUtil { fun getTopForDirectiveDeclarationClassType( topForDirectiveItem: PsiElement, project: Project, + daoMethod: PsiMethod?, ): PsiParentClass? { var result: PsiParentClass? = null var fieldAccessTopParentClass: PsiParentClass? = null @@ -256,8 +291,8 @@ class ForDirectiveUtil { ) } else { // Defined by Dao parameter - val file = topForDirectiveItem.containingFile ?: return null - val daoMethod = findDaoMethod(file) ?: return null + if (daoMethod == null) return null + val topElementText = forDirectiveDeclaration.getDeclarationChildren().firstOrNull()?.text ?: return null @@ -343,9 +378,10 @@ class ForDirectiveUtil { val convertOptional = PsiClassTypeUtil.convertOptionalType(topParent.type, project) PsiParentClass(convertOptional) } - // TODO: Display an error message that the property cannot be called. val parentType = PsiClassTypeUtil.convertOptionalType(parent.type, project) - val classType = parentType as? PsiClassType ?: return null + val classType = + parentType as? PsiClassType + ?: return ValidationNotFoundTopTypeResult(blocks.first(), shortName) var competeResult: ValidationCompleteResult? = null diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionFieldAccessVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionFieldAccessVisitorProcessor.kt similarity index 91% rename from src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionFieldAccessVisitorProcessor.kt rename to src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionFieldAccessVisitorProcessor.kt index f409c0a5..439a564d 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionFieldAccessVisitorProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionFieldAccessVisitorProcessor.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.inspection.sql.handler +package org.domaframework.doma.intellij.inspection.sql.processor import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiFile @@ -21,7 +21,6 @@ import org.domaframework.doma.intellij.common.dao.findDaoMethod import org.domaframework.doma.intellij.common.psi.PsiDaoMethod import org.domaframework.doma.intellij.common.psi.PsiParentClass import org.domaframework.doma.intellij.common.sql.cleanString -import org.domaframework.doma.intellij.common.sql.validator.result.ValidationForDirectiveItemTypeResult import org.domaframework.doma.intellij.common.util.ForDirectiveUtil import org.domaframework.doma.intellij.extension.expr.accessElements import org.domaframework.doma.intellij.extension.psi.findParameter @@ -54,10 +53,8 @@ class InspectionFieldAccessVisitorProcessor( if (forItem != null) { val result = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks, forItem) if (result == null) { - ValidationForDirectiveItemTypeResult( - topElement, - this.shortName, - ).highlightElement(holder) + // If the for directive definition element of the top element is invalid, + // an error is displayed by InspectionPrimaryVisitorProcessor. return } val specifiedClassType = diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionForDirectiveVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionForDirectiveVisitorProcessor.kt new file mode 100644 index 00000000..791ea0b1 --- /dev/null +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionForDirectiveVisitorProcessor.kt @@ -0,0 +1,41 @@ +/* + * 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.sql.processor + +import com.intellij.codeInspection.ProblemsHolder +import org.domaframework.doma.intellij.common.sql.validator.result.ValidationForDirectiveItemTypeResult +import org.domaframework.doma.intellij.common.util.ForDirectiveUtil +import org.domaframework.doma.intellij.extension.psi.getForItem +import org.domaframework.doma.intellij.psi.SqlElForDirective + +class InspectionForDirectiveVisitorProcessor( + val shortName: String, + private val element: SqlElForDirective, +) : InspectionVisitorProcessor(shortName) { + fun check(holder: ProblemsHolder) { + val forItem = element.getForItem() ?: return + val directiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(forItem, false) + val declarationType = + ForDirectiveUtil.getForDirectiveItemClassType(element.project, directiveBlocks) + + if (declarationType == null) { + ValidationForDirectiveItemTypeResult( + forItem, + this.shortName, + ).highlightElement(holder) + } + } +} diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionPrimaryVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionPrimaryVisitorProcessor.kt similarity index 96% rename from src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionPrimaryVisitorProcessor.kt rename to src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionPrimaryVisitorProcessor.kt index 4bacf900..37d7130c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionPrimaryVisitorProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionPrimaryVisitorProcessor.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.inspection.sql.handler +package org.domaframework.doma.intellij.inspection.sql.processor import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiFile @@ -50,6 +50,8 @@ class InspectionPrimaryVisitorProcessor( skipSelf = isSkip, forDirectives = forDirectiveBlocks, ) + if (forDirectiveExp?.getForItem() == element) return + if (forItem != null) { val forDeclarationType = ForDirectiveUtil.getForDirectiveItemClassType( diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionStaticFieldAccessVisitorProcessor.kt similarity index 97% rename from src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt rename to src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionStaticFieldAccessVisitorProcessor.kt index 2e99a998..79b82c2a 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionStaticFieldAccessVisitorProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionStaticFieldAccessVisitorProcessor.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.inspection.sql.handler +package org.domaframework.doma.intellij.inspection.sql.processor import com.intellij.codeInspection.ProblemsHolder import org.domaframework.doma.intellij.common.psi.PsiStaticElement diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionVisitorProcessor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionVisitorProcessor.kt similarity index 96% rename from src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionVisitorProcessor.kt rename to src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionVisitorProcessor.kt index a1b235e5..8be0d357 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/handler/InspectionVisitorProcessor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/processor/InspectionVisitorProcessor.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.domaframework.doma.intellij.inspection.sql.handler +package org.domaframework.doma.intellij.inspection.sql.processor import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.PsiElement diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt index abc7e851..9b8c0e7c 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/sql/visitor/SqlInspectionVisitor.kt @@ -23,10 +23,12 @@ import com.intellij.psi.util.elementType import org.domaframework.doma.intellij.common.isInjectionSqlFile import org.domaframework.doma.intellij.common.isJavaOrKotlinFileType import org.domaframework.doma.intellij.extension.psi.isFirstElement -import org.domaframework.doma.intellij.inspection.sql.handler.InspectionFieldAccessVisitorProcessor -import org.domaframework.doma.intellij.inspection.sql.handler.InspectionPrimaryVisitorProcessor -import org.domaframework.doma.intellij.inspection.sql.handler.InspectionStaticFieldAccessVisitorProcessor +import org.domaframework.doma.intellij.inspection.sql.processor.InspectionFieldAccessVisitorProcessor +import org.domaframework.doma.intellij.inspection.sql.processor.InspectionForDirectiveVisitorProcessor +import org.domaframework.doma.intellij.inspection.sql.processor.InspectionPrimaryVisitorProcessor +import org.domaframework.doma.intellij.inspection.sql.processor.InspectionStaticFieldAccessVisitorProcessor import org.domaframework.doma.intellij.psi.SqlElFieldAccessExpr +import org.domaframework.doma.intellij.psi.SqlElForDirective import org.domaframework.doma.intellij.psi.SqlElPrimaryExpr import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr import org.domaframework.doma.intellij.psi.SqlTypes @@ -50,8 +52,8 @@ class SqlInspectionVisitor( override fun visitElStaticFieldAccessExpr(element: SqlElStaticFieldAccessExpr) { super.visitElStaticFieldAccessExpr(element) - val handler = InspectionStaticFieldAccessVisitorProcessor(this.shortName) - handler.check(element, holder) + val processor = InspectionStaticFieldAccessVisitorProcessor(this.shortName) + processor.check(element, holder) } override fun visitElFieldAccessExpr(element: SqlElFieldAccessExpr) { @@ -59,8 +61,14 @@ class SqlInspectionVisitor( if (setFile(element)) return val visitFile: PsiFile = file ?: return - val handler = InspectionFieldAccessVisitorProcessor(shortName, element) - handler.check(holder, visitFile) + val processor = InspectionFieldAccessVisitorProcessor(shortName, element) + processor.check(holder, visitFile) + } + + override fun visitElForDirective(element: SqlElForDirective) { + super.visitElForDirective(element) + val process = InspectionForDirectiveVisitorProcessor(shortName, element) + process.check(holder) } override fun visitElPrimaryExpr(element: SqlElPrimaryExpr) { @@ -69,7 +77,7 @@ class SqlInspectionVisitor( if (setFile(element)) return val visitFile: PsiFile = file ?: return - val handler = InspectionPrimaryVisitorProcessor(this.shortName, element) - handler.check(holder, visitFile) + val processor = InspectionPrimaryVisitorProcessor(this.shortName, element) + processor.check(holder, visitFile) } } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElIdExprReference.kt b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElIdExprReference.kt index 8e2970f0..d8fe4261 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElIdExprReference.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/reference/SqlElIdExprReference.kt @@ -48,7 +48,7 @@ class SqlElIdExprReference( // Refers to an element defined in the for directive val isSelfSkip = isSelfSkip(topElm) val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(element, isSelfSkip) - val forItem = ForDirectiveUtil.findForItem(element, forDirectives = forDirectiveBlocks) + val forItem = ForDirectiveUtil.findForItem(topElm, forDirectives = forDirectiveBlocks) if (forItem != null && element.textOffset == topElm.textOffset) { PluginLoggerUtil.countLogging( this::class.java.simpleName, @@ -69,18 +69,29 @@ class SqlElIdExprReference( ) } - val tolElementForItem = - ForDirectiveUtil.getForDirectiveItemClassType(topElm.project, forDirectiveBlocks) + // Reference to field access elements + var parentClass: PsiParentClass? = null var isBatchAnnotation = false - var parentClass = - if (tolElementForItem != null) { - tolElementForItem - } else { - val daoMethod = findDaoMethod(file) ?: return null - val param = daoMethod.findParameter(topElm.text) ?: return null - isBatchAnnotation = PsiDaoMethod(topElm.project, daoMethod).daoType.isBatchAnnotation() - PsiParentClass(param.type) - } + if (forItem != null) { + val project = topElm.project + val forItemClassType = + ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks, forItem) + ?: return null + val specifiedClassType = + ForDirectiveUtil.resolveForDirectiveItemClassTypeBySuffixElement(topElm.text) + parentClass = + if (specifiedClassType != null) { + PsiParentClass(specifiedClassType) + } else { + forItemClassType + } + } else { + val daoMethod = findDaoMethod(file) ?: return null + val param = daoMethod.findParameter(topElm.text) ?: return null + parentClass = PsiParentClass(param.type) + isBatchAnnotation = PsiDaoMethod(topElm.project, daoMethod).daoType.isBatchAnnotation() + } + val result = ForDirectiveUtil.getFieldAccessLastPropertyClassType( targetElements, diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/accessStaticProperty.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/accessStaticProperty.sql index 6b0af1b3..89390917 100644 --- a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/accessStaticProperty.sql +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/accessStaticProperty.sql @@ -33,7 +33,7 @@ where /*%end */ ) -- Static field call that does not exist - /*%if @doma.example.entity.ProjectDetail@priority >= 3 */ + /*%if @doma.example.entity.ProjectDetail@priority >= 3 */ -- Static method call that does not exist - AND pd.limit_date = /* @doma.example.entity.ProjectDetail@getLimit() */0 + AND pd.limit_date = /* @doma.example.entity.ProjectDetail@getLimit() */0 /*%end */ \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForNonEntityClass.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForNonEntityClass.sql index 3c5cee80..0019dc19 100644 --- a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForNonEntityClass.sql +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForNonEntityClass.sql @@ -25,9 +25,9 @@ AND pe.end_date >= CURRENT_DATE /*%end*/ -- Reference error for a non-existent field - /*%for child : employee.projectIds */ + /*%for child : employee.projectIds */ -- An error occurred because the referenced element was not correctly defined. - AND pe.parent_project = /* child.projectId */0 + AND pe.parent_project = /* child.projectId */0 -- Reference error for a non-existent method AND pe.member_id IN /* employee.getTopProject() */(0,1,2) /*%end */ diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/callStaticPropertyPackageName.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/callStaticPropertyPackageName.sql index 2c90eb10..8ff52613 100644 --- a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/callStaticPropertyPackageName.sql +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/callStaticPropertyPackageName.sql @@ -1,4 +1,4 @@ Select COUNT(*) from employee - where rank = /* @doma.example.entity.EmployeeBase.Rank@MANAGER */1 - and exist_class = /* @doma.example.entity.Employee.Exists@T */false - and exist_pkg = /* @doma.example.entity@T */false \ No newline at end of file + where rank = /* @doma.example.entity.EmployeeBase.Rank@MANAGER */1 + and exist_class = /* @doma.example.entity.Employee.Exists@T */false + and exist_pkg = /* @doma.example.entity@T */false \ No newline at end of file From 9bbb64f4a49ca4bbe61f03d69be2e449e0e2c0ff Mon Sep 17 00:00:00 2001 From: xterao Date: Wed, 14 May 2025 18:14:37 +0900 Subject: [PATCH 7/8] Add error messages test case --- .../doma/intellij/inspection/dao/DomaUseVariableTest.kt | 1 + .../example/dao/DaoMethodVariableInspectionTestDao.java | 6 ++++++ .../duplicateForDirectiveDefinitionNames.sql | 9 +++++++++ .../bindVariableForItemHasNextAndIndex.sql | 2 +- 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/test/testData/src/main/resources/META-INF/doma/example/dao/DaoMethodVariableInspectionTestDao/duplicateForDirectiveDefinitionNames.sql diff --git a/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/DomaUseVariableTest.kt b/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/DomaUseVariableTest.kt index 910056de..c939953b 100644 --- a/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/DomaUseVariableTest.kt +++ b/src/test/kotlin/org/domaframework/doma/intellij/inspection/dao/DomaUseVariableTest.kt @@ -35,6 +35,7 @@ class DomaUseVariableTest : DomaSqlTest() { "$testDaoName/collectDoesNotCauseError.sql", "$testDaoName/collectDoesCauseError.sql", "$testDaoName/noErrorWhenUsedInFunctionParameters.sql", + "$testDaoName/duplicateForDirectiveDefinitionNames.sql", ) myFixture.enableInspections(DaoMethodVariableInspector()) } diff --git a/src/test/testData/src/main/java/doma/example/dao/DaoMethodVariableInspectionTestDao.java b/src/test/testData/src/main/java/doma/example/dao/DaoMethodVariableInspectionTestDao.java index 05da5b26..aec91b45 100644 --- a/src/test/testData/src/main/java/doma/example/dao/DaoMethodVariableInspectionTestDao.java +++ b/src/test/testData/src/main/java/doma/example/dao/DaoMethodVariableInspectionTestDao.java @@ -50,4 +50,10 @@ interface DaoMethodVariableInspectionTestDao { @Select Project noErrorWhenUsedInFunctionParameters(Employee employee, Integer count); + @Select + Employee duplicateForDirectiveDefinitionNames(Employee member, Integer count, + List users, + String searchName, + Boolean inForm); + } \ No newline at end of file diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/DaoMethodVariableInspectionTestDao/duplicateForDirectiveDefinitionNames.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/DaoMethodVariableInspectionTestDao/duplicateForDirectiveDefinitionNames.sql new file mode 100644 index 00000000..42db98e9 --- /dev/null +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/DaoMethodVariableInspectionTestDao/duplicateForDirectiveDefinitionNames.sql @@ -0,0 +1,9 @@ +SELECT * + FROM users + WHERE count = /* ids.size() */0 + /*%for member : users */ + OR (id = /* member.userId */0 + AND count < /* users.size() */0) + AND form = /* inForm */false + /*%end */ + AND searchName = /* searchName */'search' diff --git a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForItemHasNextAndIndex.sql b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForItemHasNextAndIndex.sql index 0b2c6733..44cf89d9 100644 --- a/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForItemHasNextAndIndex.sql +++ b/src/test/testData/src/main/resources/META-INF/doma/example/dao/EmployeeSummaryDao/bindVariableForItemHasNextAndIndex.sql @@ -18,7 +18,7 @@ select p.project_id /*%end */ p.employee_id = /* member.employeeId */0 and p.not_next = /* member_has_next */false - and p.next = /* member_has_next */false + and p.next = /* member_has_next.a */false and p.not_index = /* member_index */999 and p.index = /* member_index */0 /*%end */ From a6993ba2abe3e4f91b3ab55d2b15e509ae804f9c Mon Sep 17 00:00:00 2001 From: xterao Date: Wed, 14 May 2025 18:21:34 +0900 Subject: [PATCH 8/8] Refactor inspection error messages for DAO and SQL validations --- .../sql/validator/result/ValidationDaoParamResult.kt | 2 +- .../sql/validator/result/ValidationPropertyResult.kt | 2 +- .../dao/inspector/DaoMethodVariableInspector.kt | 2 +- .../inspection/dao/inspector/SqlFileExistInspector.kt | 2 +- src/main/resources/messages/DomaToolsBundle.properties | 8 ++++---- src/main/resources/messages/DomaToolsBundle_ja.properties | 8 ++++---- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationDaoParamResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationDaoParamResult.kt index e22d0355..6ff281ba 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationDaoParamResult.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationDaoParamResult.kt @@ -41,7 +41,7 @@ open class ValidationDaoParamResult( holder.registerProblem( identify, MessageBundle.message( - "inspector.invalid.dao.parameter", + "inspection.invalid.dao.parameter", daoName, identify.text ?: "", ), diff --git a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationPropertyResult.kt b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationPropertyResult.kt index 13eb6208..55fb1b55 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationPropertyResult.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationPropertyResult.kt @@ -46,7 +46,7 @@ class ValidationPropertyResult( holder.registerProblem( identify, MessageBundle.message( - "inspector.invalid.class.property", + "inspection.invalid.sql.property", parentName, identify.text ?: "", ), diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/DaoMethodVariableInspector.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/DaoMethodVariableInspector.kt index c172824c..d8cf70a7 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/DaoMethodVariableInspector.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/DaoMethodVariableInspector.kt @@ -93,7 +93,7 @@ class DaoMethodVariableInspector : AbstractBaseJavaLocalInspectionTool() { MessageBundle.message("inspection.invalid.dao.duplicate") } else { MessageBundle.message( - "inspection.dao.method.variable.error", + "inspection.invalid.dao.paramUse", param.name, ) } diff --git a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/SqlFileExistInspector.kt b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/SqlFileExistInspector.kt index a737ec46..b1ea94d6 100644 --- a/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/SqlFileExistInspector.kt +++ b/src/main/kotlin/org/domaframework/doma/intellij/inspection/dao/inspector/SqlFileExistInspector.kt @@ -68,7 +68,7 @@ class SqlFileExistInspector : AbstractBaseJavaLocalInspectionTool() { if (psiDaoMethod.sqlFile == null) { problemHolder.registerProblem( identifier, - MessageBundle.message("inspection.sql.not.exist.error"), + MessageBundle.message("inspection.invalid.dao.notExistSql"), ProblemHighlightType.ERROR, GenerateSQLFileQuickFixFactory.createSql(psiDaoMethod), ) diff --git a/src/main/resources/messages/DomaToolsBundle.properties b/src/main/resources/messages/DomaToolsBundle.properties index ca5ee82f..6f377056 100644 --- a/src/main/resources/messages/DomaToolsBundle.properties +++ b/src/main/resources/messages/DomaToolsBundle.properties @@ -1,11 +1,11 @@ jump.to.sql.tooltip.title=Open SQL file jump.to.dao.tooltip.title=Jump to Dao method definition generate.sql.quickfix.title=Create SQL file -inspection.sql.not.exist.error=SQL file does not exist -inspector.invalid.class.property=The field or method [{1}] does not exist in the class [{0}] -inspection.dao.method.variable.error=There are unused parameters in the SQL [{0}] -inspector.invalid.dao.parameter=The bind variable [{1}] does not exist in the Dao method [{0}] config.enable.sql.format=Enable SQL Format +inspection.invalid.dao.notExistSql=SQL file does not exist +inspection.invalid.sql.property=The field or method [{1}] does not exist in the class [{0}] +inspection.invalid.dao.paramUse=There are unused parameters in the SQL [{0}] +inspection.invalid.dao.parameter=The bind variable [{1}] does not exist in the Dao method [{0}] inspection.invalid.sql.topType=Can't get type of first element inspection.invalid.sql.staticProperty=[{0}] is not a public or static property in the class [{1}] inspection.invalid.sql.testdata=Bind variables must be followed by test data diff --git a/src/main/resources/messages/DomaToolsBundle_ja.properties b/src/main/resources/messages/DomaToolsBundle_ja.properties index dd7b846f..b21f366a 100644 --- a/src/main/resources/messages/DomaToolsBundle_ja.properties +++ b/src/main/resources/messages/DomaToolsBundle_ja.properties @@ -1,11 +1,11 @@ jump.to.sql.tooltip.title=SQL\u30D5\u30A1\u30A4\u30EB\u3092\u958B\u304F jump.to.dao.tooltip.title=Dao\u30E1\u30BD\u30C3\u30C9\u5B9A\u7FA9\u306B\u9077\u79FB\u3059\u308B generate.sql.quickfix.title=SQL\u30D5\u30A1\u30A4\u30EB\u3092\u4F5C\u6210 -inspection.sql.not.exist.error=SQL\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u305B\u3093 -inspector.invalid.class.property=\u30AF\u30E9\u30B9[{0}]\u306B\u5B58\u5728\u3057\u306A\u3044\u30D5\u30A3\u30FC\u30EB\u30C9\u3001\u307E\u305F\u306F\u30E1\u30BD\u30C3\u30C9\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059:[{1}] -inspection.dao.method.variable.error=SQL\u3067\u4F7F\u7528\u3055\u308C\u3066\u3044\u306A\u3044\u5F15\u6570\u304C\u3042\u308A\u307E\u3059:[{0}] -inspector.invalid.dao.parameter=Dao\u30E1\u30BD\u30C3\u30C9[{0}]\u306B\u5B58\u5728\u3057\u306A\u3044\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059:[{1}] config.enable.sql.format=SQL\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u3092\u6709\u52B9\u5316 +inspection.invalid.dao.notExistSql=SQL\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u305B\u3093 +inspection.invalid.sql.property=\u30AF\u30E9\u30B9[{0}]\u306B\u5B58\u5728\u3057\u306A\u3044\u30D5\u30A3\u30FC\u30EB\u30C9\u3001\u307E\u305F\u306F\u30E1\u30BD\u30C3\u30C9\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059:[{1}] +inspection.invalid.dao.paramUse=SQL\u3067\u4F7F\u7528\u3055\u308C\u3066\u3044\u306A\u3044\u5F15\u6570\u304C\u3042\u308A\u307E\u3059:[{0}] +inspection.invalid.dao.parameter=Dao\u30E1\u30BD\u30C3\u30C9[{0}]\u306B\u5B58\u5728\u3057\u306A\u3044\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059:[{1}] inspection.invalid.sql.topType=\u6700\u521D\u306E\u8981\u7D20\u306E\u578B\u304C\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093 inspection.invalid.sql.staticProperty=[{0}]\u0020\u306F\u30AF\u30E9\u30B9\u0020[{1}]\u0020\u306E\u0020\u0070\u0075\u0062\u006C\u0069\u0063\u0020\u307E\u305F\u0020\u0073\u0074\u0061\u0074\u0069\u0063\u0020\u30D7\u30ED\u30D1\u30C6\u30A3\u3067\u306F\u3042\u308A\u307E\u305B\u3093 inspection.invalid.sql.testdata=\u30D0\u30A4\u30F3\u30C9\u5909\u6570\u306E\u5F8C\u308D\u306B\u306F\u30C6\u30B9\u30C8\u30C7\u30FC\u30BF\u304C\u5FC5\u8981\u3067\u3059