Skip to content

Commit d801f11

Browse files
authored
Merge pull request #162 from domaframework/feature/more-detailed-error-messages
Error message details
2 parents 289f94e + a6993ba commit d801f11

27 files changed

+685
-244
lines changed

src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationDaoParamResult.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ open class ValidationDaoParamResult(
4141
holder.registerProblem(
4242
identify,
4343
MessageBundle.message(
44-
"inspector.invalid.dao.parameter",
44+
"inspection.invalid.dao.parameter",
4545
daoName,
4646
identify.text ?: "",
4747
),
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.sql.validator.result
17+
18+
import com.intellij.codeInspection.ProblemsHolder
19+
import com.intellij.openapi.project.Project
20+
import com.intellij.openapi.util.TextRange
21+
import com.intellij.psi.PsiElement
22+
import org.domaframework.doma.intellij.bundle.MessageBundle
23+
import org.domaframework.doma.intellij.common.psi.PsiParentClass
24+
25+
class ValidationForDirectiveItemTypeResult(
26+
override val identify: PsiElement?,
27+
override val shortName: String = "",
28+
) : ValidationResult(identify, null, shortName) {
29+
override fun setHighlight(
30+
highlightRange: TextRange,
31+
identify: PsiElement,
32+
holder: ProblemsHolder,
33+
parent: PsiParentClass?,
34+
project: Project,
35+
) {
36+
val project = identify.project
37+
holder.registerProblem(
38+
identify,
39+
MessageBundle.message("inspection.invalid.sql.iterable"),
40+
problemHighlightType(project, shortName),
41+
highlightRange,
42+
)
43+
}
44+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.sql.validator.result
17+
18+
import com.intellij.codeInspection.ProblemsHolder
19+
import com.intellij.openapi.project.Project
20+
import com.intellij.openapi.util.TextRange
21+
import com.intellij.psi.PsiElement
22+
import org.domaframework.doma.intellij.bundle.MessageBundle
23+
import org.domaframework.doma.intellij.common.psi.PsiParentClass
24+
import org.domaframework.doma.intellij.psi.SqlElClass
25+
26+
class ValidationNotFoundStaticPropertyResult(
27+
override val identify: PsiElement?,
28+
val clazz: SqlElClass,
29+
override val shortName: String = "",
30+
) : ValidationResult(identify, null, shortName) {
31+
override fun setHighlight(
32+
highlightRange: TextRange,
33+
identify: PsiElement,
34+
holder: ProblemsHolder,
35+
parent: PsiParentClass?,
36+
project: Project,
37+
) {
38+
val project = identify.project
39+
holder.registerProblem(
40+
identify,
41+
MessageBundle.message(
42+
"inspection.invalid.sql.staticProperty",
43+
identify.text,
44+
clazz.text,
45+
),
46+
problemHighlightType(project, shortName),
47+
highlightRange,
48+
)
49+
}
50+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright Doma Tools Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.domaframework.doma.intellij.common.sql.validator.result
17+
18+
import com.intellij.codeInspection.ProblemsHolder
19+
import com.intellij.openapi.project.Project
20+
import com.intellij.openapi.util.TextRange
21+
import com.intellij.psi.PsiElement
22+
import org.domaframework.doma.intellij.bundle.MessageBundle
23+
import org.domaframework.doma.intellij.common.psi.PsiParentClass
24+
25+
class ValidationNotFoundTopTypeResult(
26+
override val identify: PsiElement?,
27+
override val shortName: String = "",
28+
) : ValidationResult(identify, null, shortName) {
29+
override fun setHighlight(
30+
highlightRange: TextRange,
31+
identify: PsiElement,
32+
holder: ProblemsHolder,
33+
parent: PsiParentClass?,
34+
project: Project,
35+
) {
36+
val project = identify.project
37+
holder.registerProblem(
38+
identify,
39+
MessageBundle.message("inspection.invalid.sql.topType"),
40+
problemHighlightType(project, shortName),
41+
highlightRange,
42+
)
43+
}
44+
}

src/main/kotlin/org/domaframework/doma/intellij/common/sql/validator/result/ValidationPropertyResult.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class ValidationPropertyResult(
4646
holder.registerProblem(
4747
identify,
4848
MessageBundle.message(
49-
"inspector.invalid.class.property",
49+
"inspection.invalid.sql.property",
5050
parentName,
5151
identify.text ?: "",
5252
),

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

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.intellij.openapi.project.Project
1919
import com.intellij.psi.PsiClass
2020
import com.intellij.psi.PsiClassType
2121
import com.intellij.psi.PsiElement
22+
import com.intellij.psi.PsiMethod
2223
import com.intellij.psi.PsiType
2324
import com.intellij.psi.PsiTypes
2425
import com.intellij.psi.util.CachedValue
@@ -34,6 +35,7 @@ import org.domaframework.doma.intellij.common.sql.PsiClassTypeUtil
3435
import org.domaframework.doma.intellij.common.sql.cleanString
3536
import org.domaframework.doma.intellij.common.sql.foritem.ForItem
3637
import org.domaframework.doma.intellij.common.sql.validator.result.ValidationCompleteResult
38+
import org.domaframework.doma.intellij.common.sql.validator.result.ValidationNotFoundTopTypeResult
3739
import org.domaframework.doma.intellij.common.sql.validator.result.ValidationPropertyResult
3840
import org.domaframework.doma.intellij.common.sql.validator.result.ValidationResult
3941
import org.domaframework.doma.intellij.extension.expr.accessElements
@@ -173,39 +175,77 @@ class ForDirectiveUtil {
173175
fun getForDirectiveItemClassType(
174176
project: Project,
175177
forDirectiveBlocks: List<BlockToken>,
178+
targetForItem: PsiElement? = null,
176179
): PsiParentClass? {
177180
// Get the type of the top for directive definition element
178181
// Defined in Dao parameters or static property calls
179182
if (forDirectiveBlocks.isEmpty()) return null
183+
val topDirectiveItem = forDirectiveBlocks.first().item
184+
val file = topDirectiveItem.containingFile ?: return null
185+
val daoMethod = findDaoMethod(file)
186+
180187
var parentClassType =
181188
getTopForDirectiveDeclarationClassType(
182189
forDirectiveBlocks.first().item,
183190
project,
184-
) ?: return null
191+
daoMethod,
192+
)
185193
forDirectiveBlocks.drop(1).forEach { directive ->
186194
// Get the definition type of the target directive
187195
val formItem = ForItem(directive.item)
196+
if (targetForItem != null && formItem.element.textOffset > targetForItem.textOffset) {
197+
return parentClassType
198+
}
188199
val forDirectiveExpr = formItem.getParentForDirectiveExpr()
189200
val forDirectiveDeclaration = forDirectiveExpr?.getForItemDeclaration()
190201
if (forDirectiveDeclaration != null) {
191-
val forItemDeclarationBlocks = forDirectiveDeclaration.getDeclarationChildren()
192-
getFieldAccessLastPropertyClassType(
193-
forItemDeclarationBlocks,
194-
project,
195-
parentClassType,
196-
complete = { lastType ->
197-
val classType = lastType.type as? PsiClassType
198-
val nestClass =
199-
if (classType != null &&
200-
PsiClassTypeUtil.Companion.isIterableType(classType, project)
201-
) {
202-
classType.parameters.firstOrNull()
203-
} else {
204-
null
205-
}
206-
nestClass?.let { parentClassType = PsiParentClass(it) }
207-
},
208-
)
202+
val declarationTopElement =
203+
forDirectiveDeclaration.getDeclarationChildren().first()
204+
val findDeclarationForItem =
205+
findForItem(declarationTopElement, forDirectives = forDirectiveBlocks)
206+
207+
if (findDeclarationForItem == null && daoMethod != null) {
208+
val matchParam = daoMethod.findParameter(declarationTopElement.text)
209+
if (matchParam != null) {
210+
val convertOptional =
211+
PsiClassTypeUtil.convertOptionalType(matchParam.type, project)
212+
parentClassType = PsiParentClass(convertOptional)
213+
}
214+
}
215+
if (parentClassType != null) {
216+
val isBatchAnnotation = daoMethod?.let { PsiDaoMethod(project, it).daoType.isBatchAnnotation() } == true
217+
val forItemDeclarationBlocks =
218+
forDirectiveDeclaration.getDeclarationChildren()
219+
getFieldAccessLastPropertyClassType(
220+
forItemDeclarationBlocks,
221+
project,
222+
parentClassType,
223+
isBatchAnnotation = isBatchAnnotation,
224+
complete = { lastType ->
225+
val classType = lastType.type as? PsiClassType
226+
val nestClass =
227+
if (classType != null &&
228+
PsiClassTypeUtil.Companion.isIterableType(
229+
classType,
230+
project,
231+
)
232+
) {
233+
classType.parameters.firstOrNull()
234+
} else {
235+
null
236+
}
237+
parentClassType =
238+
if (nestClass != null) {
239+
PsiParentClass(nestClass)
240+
} else {
241+
null
242+
}
243+
},
244+
)
245+
if (targetForItem != null && formItem.element.text == targetForItem.text) {
246+
return parentClassType
247+
}
248+
}
209249
}
210250
}
211251

@@ -215,6 +255,7 @@ class ForDirectiveUtil {
215255
fun getTopForDirectiveDeclarationClassType(
216256
topForDirectiveItem: PsiElement,
217257
project: Project,
258+
daoMethod: PsiMethod?,
218259
): PsiParentClass? {
219260
var result: PsiParentClass? = null
220261
var fieldAccessTopParentClass: PsiParentClass? = null
@@ -250,8 +291,8 @@ class ForDirectiveUtil {
250291
)
251292
} else {
252293
// Defined by Dao parameter
253-
val file = topForDirectiveItem.containingFile ?: return null
254-
val daoMethod = findDaoMethod(file) ?: return null
294+
if (daoMethod == null) return null
295+
255296
val topElementText =
256297
forDirectiveDeclaration.getDeclarationChildren().firstOrNull()?.text
257298
?: return null
@@ -337,9 +378,10 @@ class ForDirectiveUtil {
337378
val convertOptional = PsiClassTypeUtil.convertOptionalType(topParent.type, project)
338379
PsiParentClass(convertOptional)
339380
}
340-
// TODO: Display an error message that the property cannot be called.
341381
val parentType = PsiClassTypeUtil.convertOptionalType(parent.type, project)
342-
val classType = parentType as? PsiClassType ?: return null
382+
val classType =
383+
parentType as? PsiClassType
384+
?: return ValidationNotFoundTopTypeResult(blocks.first(), shortName)
343385

344386
var competeResult: ValidationCompleteResult? = null
345387

src/main/kotlin/org/domaframework/doma/intellij/contributor/sql/provider/SqlParameterCompletionProvider.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,9 +421,9 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
421421
): Boolean {
422422
val project = top.project
423423
val forDirectiveBlocks = ForDirectiveUtil.getForDirectiveBlocks(top)
424-
ForDirectiveUtil.findForItem(top, forDirectives = forDirectiveBlocks) ?: return false
424+
val forItem = ForDirectiveUtil.findForItem(top, forDirectives = forDirectiveBlocks) ?: return false
425425

426-
val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks) ?: return false
426+
val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectiveBlocks, forItem) ?: return false
427427
val specifiedClassType = ForDirectiveUtil.resolveForDirectiveItemClassTypeBySuffixElement(top.text)
428428
val topClassType =
429429
if (specifiedClassType != null) {

src/main/kotlin/org/domaframework/doma/intellij/document/generator/DocumentDaoParameterGenerator.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ class DocumentDaoParameterGenerator(
4545
val searchElement = fieldAccessBlocks?.firstOrNull() ?: originalElement
4646

4747
var isBatchAnnotation = false
48-
if (ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives) != null) {
49-
val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives)
48+
val forItem = ForDirectiveUtil.findForItem(searchElement, forDirectives = forDirectives)
49+
if (forItem != null) {
50+
val forItemClassType = ForDirectiveUtil.getForDirectiveItemClassType(project, forDirectives, forItem)
5051
val specifiedClassType =
5152
ForDirectiveUtil.resolveForDirectiveItemClassTypeBySuffixElement(
5253
searchElement.text,

0 commit comments

Comments
 (0)