@@ -26,19 +26,21 @@ import com.intellij.psi.util.elementType
2626import org.domaframework.doma.intellij.common.psi.PsiParentClass
2727import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil
2828import org.domaframework.doma.intellij.common.sql.foritem.ForDeclarationDaoBaseItem
29+ import org.domaframework.doma.intellij.common.sql.foritem.ForDeclarationItem
30+ import org.domaframework.doma.intellij.common.sql.foritem.ForDeclarationStaticFieldAccessorItem
2931import org.domaframework.doma.intellij.common.sql.foritem.ForDirectiveItemBase
3032import org.domaframework.doma.intellij.common.sql.foritem.ForItem
3133import org.domaframework.doma.intellij.common.sql.validator.SqlElForItemFieldAccessorChildElementValidator
3234import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult
3335import org.domaframework.doma.intellij.common.sql.validator.result.ValidationDaoParamResult
34- import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult
3536import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult
37+ import org.domaframework.doma.intellij.extension.expr.accessElements
3638import org.domaframework.doma.intellij.extension.psi.findParameter
3739import org.domaframework.doma.intellij.extension.psi.getDomaAnnotationType
3840import org.domaframework.doma.intellij.extension.psi.getForItem
3941import org.domaframework.doma.intellij.extension.psi.getForItemDeclaration
4042import org.domaframework.doma.intellij.psi.SqlElForDirective
41- import org.domaframework.doma.intellij.psi.SqlElIdExpr
43+ import org.domaframework.doma.intellij.psi.SqlElStaticFieldAccessExpr
4244import org.domaframework.doma.intellij.psi.SqlTypes
4345
4446class ForDirectiveInspection (
@@ -57,8 +59,6 @@ class ForDirectiveInspection(
5759 END ,
5860 }
5961
60- var declarationFieldElements = mutableListOf<SqlElIdExpr >()
61-
6262 private val cachedForDirectiveBlocks: MutableMap <PsiElement , CachedValue <List <BlockToken >>> =
6363 mutableMapOf ()
6464
@@ -87,97 +87,90 @@ class ForDirectiveInspection(
8787 val targetElement: PsiElement = blockElements.firstOrNull() ? : return null
8888 val topElm = blockElements.first()
8989
90- val firDirectives = getForDirectiveBlock(targetElement)
91- val forItem = getForItem(targetElement, firDirectives)
92- var errorElement: ValidationResult ? = ValidationDaoParamResult (targetElement, " " , shortName)
90+ val forDirectives = getForDirectiveBlock(targetElement)
91+ val forItem = getForItem(targetElement, forDirectives) ? : return createErrorResult(targetElement)
9392 val domaAnnotationType = daoMethod.getDomaAnnotationType()
9493
95- if (forItem != null ) {
96- val declarationItem =
97- getDeclarationTopItem(forItem, 0 )
94+ val declarationItem = getDeclarationTopItem(forItem, 0 )
95+ if (declarationItem !is ForDeclarationDaoBaseItem ) return createErrorResult(targetElement)
9896
99- if (declarationItem != null && declarationItem is ForDeclarationDaoBaseItem ) {
100- val daoParamDeclarativeType =
101- declarationItem.getPsiParentClass()
102- ? : return ValidationDaoParamResult (targetElement, daoMethod.name, shortName)
97+ val daoParamDeclarativeType =
98+ declarationItem.getPsiParentClass()
99+ ? : return ValidationDaoParamResult (targetElement, daoMethod.name, shortName)
103100
104- ( daoParamDeclarativeType.type as ? PsiClassType )
105- ?. let { if ( ! PsiClassTypeUtil .isIterableType(it , topElm.project)) return null }
101+ val initialType = daoParamDeclarativeType.type as ? PsiClassType
102+ if (initialType == null || ! PsiClassTypeUtil .isIterableType(initialType , topElm.project)) return null
106103
107- val nestIndex = declarationItem.index
108- var lastType = daoParamDeclarativeType.type
109- var nestClassType : PsiClassType ? = (lastType as ? PsiClassType )
104+ val finalType = analyzeNestedForDirectives(forDirectives, initialType, domaAnnotationType.isBatchAnnotation(), topElm)
105+ return finalType?. let { ValidationCompleteResult (topElm, PsiParentClass (it)) } ? : createErrorResult(targetElement)
106+ }
110107
111- var i = 0
112- var listIndex = 1
108+ private fun createErrorResult (targetElement : PsiElement ): ValidationResult = ValidationDaoParamResult (targetElement, " " , shortName)
113109
114- if (domaAnnotationType.isBatchAnnotation()) {
115- nestClassType = nestClassType?.parameters?.firstOrNull() as ? PsiClassType ?
116- }
110+ private fun analyzeNestedForDirectives (
111+ forDirectives : List <BlockToken >,
112+ initialType : PsiClassType ,
113+ isBatchAnnotation : Boolean ,
114+ topElm : PsiElement ,
115+ ): PsiClassType ? {
116+ var nestClassType: PsiClassType ? = if (isBatchAnnotation) initialType.parameters.firstOrNull() as ? PsiClassType else initialType
117+ var listIndex = 1
117118
118- // Each time searching for the next for directive item, recursively analyze the type if the field is Access.
119- while (nestClassType != null &&
120- i <= nestIndex
121- ) {
122- // TODO: Refactoring
123- // Get the definition type for each for directive passed through
124- val targetForDirective = firDirectives[i]
125- val currentForItem = ForItem (targetForDirective.item)
126- val currentForDirectiveExpr = currentForItem.getParentForDirectiveExpr() ? : return null
127- val currentDeclaration =
128- currentForDirectiveExpr.getForItemDeclaration() ? : return null
129- val declarationChildren = currentDeclaration.getDeclarationChildren()
130- if (declarationChildren.size > 1 ) {
131- // If the for item is defined by a field access, the final type is obtained.
132- val validator =
133- SqlElForItemFieldAccessorChildElementValidator (
134- declarationChildren,
135- PsiParentClass (nestClassType),
136- shortName,
137- )
138- val currentLastType = validator.validateChildren()
139- val currentLastTypeParentType = currentLastType?.parentClass?.type as ? PsiClassType ?
140- if (currentLastTypeParentType != null ) {
141- if (! PsiClassTypeUtil .isIterableType(
142- currentLastTypeParentType,
143- topElm.project,
144- )
145- ) {
146- return ValidationPropertyResult (
147- currentForItem.element,
148- currentLastType.parentClass,
149- " " ,
150- )
151- } else {
152- nestClassType = currentLastTypeParentType.parameters.firstOrNull() as ? PsiClassType ?
153- }
154- }
155- listIndex = 1
156- } else {
157- // Get the nesting count from the List type definition obtained along the way
158- repeat(listIndex) {
159- if (nestClassType != null &&
160- PsiClassTypeUtil .isIterableType(nestClassType, topElm.project)
161- ) {
162- nestClassType =
163- nestClassType.parameters.firstOrNull() as ? PsiClassType ?
164- }
165- }
166- listIndex++
167- }
168- i++
169- }
170- return nestClassType?.let {
171- ValidationCompleteResult (
172- topElm,
173- PsiParentClass (nestClassType),
174- )
119+ for ((i, targetForDirective) in forDirectives.withIndex()) {
120+ if (nestClassType == null ) break
121+
122+ val currentForItem = ForItem (targetForDirective.item)
123+ val currentDeclaration = currentForItem.getParentForDirectiveExpr()?.getForItemDeclaration() ? : continue
124+
125+ val declarationType = processDeclarationElement(currentDeclaration, nestClassType, i)
126+ if (declarationType != null ) {
127+ if (! PsiClassTypeUtil .isIterableType(declarationType, topElm.project)) {
128+ return null
175129 }
176- ? : errorElement
130+ nestClassType = declarationType.parameters.firstOrNull() as ? PsiClassType
131+ listIndex = 1
132+ } else {
133+ nestClassType = processListType(nestClassType, listIndex, topElm)
134+ listIndex++
177135 }
178136 }
137+ return nestClassType
138+ }
139+
140+ private fun processDeclarationElement (
141+ currentDeclaration : ForDeclarationItem ,
142+ nestClassType : PsiClassType ,
143+ index : Int ,
144+ ): PsiClassType ? {
145+ val declarationElement = currentDeclaration.element
146+ if (declarationElement is SqlElStaticFieldAccessExpr ) {
147+ val forItem = ForDeclarationStaticFieldAccessorItem (declarationElement.accessElements, declarationElement, index)
148+ val declarationType = forItem.getPsiParentClass()?.type as ? PsiClassType
149+ return declarationType
150+ }
151+
152+ val declarationChildren = currentDeclaration.getDeclarationChildren()
153+ if (declarationChildren.size > 1 ) {
154+ val validator = SqlElForItemFieldAccessorChildElementValidator (declarationChildren, PsiParentClass (nestClassType), shortName)
155+ return validator.validateChildren()?.parentClass?.type as ? PsiClassType
156+ }
157+ return null
158+ }
179159
180- return errorElement
160+ private fun processListType (
161+ nestClassType : PsiClassType ,
162+ listIndex : Int ,
163+ topElm : PsiElement ,
164+ ): PsiClassType ? {
165+ var currentType: PsiClassType ? = nestClassType
166+ repeat(listIndex) {
167+ currentType?.let { type ->
168+ if (PsiClassTypeUtil .isIterableType(type, topElm.project)) {
169+ currentType = type.parameters.firstOrNull() as ? PsiClassType
170+ }
171+ }
172+ }
173+ return currentType
181174 }
182175
183176 fun getForDirectiveBlockSize (target : PsiElement ): Int = getForDirectiveBlock(target).size
@@ -249,19 +242,24 @@ class ForDirectiveInspection(
249242 searchIndex : Int = 0,
250243 ): ForDirectiveItemBase ? {
251244 val forDirectiveParent = forItem.getParentForDirectiveExpr() ? : return null
252- val declarationElement =
245+ val declarationSideElement =
253246 forDirectiveParent.getForItemDeclaration() ? : return null
254- declarationFieldElements = declarationElement.getDeclarationChildren().toMutableList()
255- val topElm = declarationFieldElements.firstOrNull() ? : return null
247+ val declarationElement = declarationSideElement.element
248+ if (declarationElement is SqlElStaticFieldAccessExpr ) {
249+ return ForDeclarationStaticFieldAccessorItem (declarationElement.accessElements, declarationElement, searchIndex)
250+ }
251+
252+ val declarationFieldElements = declarationSideElement.getDeclarationChildren().toMutableList()
253+ val declarationSideElements = declarationFieldElements.firstOrNull() ? : return null
256254
257- val parentForItem = getForItem(topElm )
255+ val parentForItem = getForItem(declarationSideElements )
258256 val index = searchIndex + 1
259257 if (parentForItem != null ) {
260258 val parentDeclaration = getDeclarationTopItem(parentForItem, index)
261259 if (parentDeclaration is ForDeclarationDaoBaseItem ) return parentDeclaration
262260 }
263261
264- val validDaoParam = daoMethod.findParameter(topElm .text)
262+ val validDaoParam = daoMethod.findParameter(declarationSideElements .text)
265263 if (validDaoParam == null ) return null
266264
267265 return ForDeclarationDaoBaseItem (
0 commit comments