Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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
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(
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,
clazz.text,
),
problemHighlightType(project, shortName),
highlightRange,
)
}
}
Original file line number Diff line number Diff line change
@@ -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,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -173,39 +175,77 @@ class ForDirectiveUtil {
fun getForDirectiveItemClassType(
project: Project,
forDirectiveBlocks: List<BlockToken>,
targetForItem: PsiElement? = null,
): PsiParentClass? {
// 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) {
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
}
}
}
}

Expand All @@ -215,6 +255,7 @@ class ForDirectiveUtil {
fun getTopForDirectiveDeclarationClassType(
topForDirectiveItem: PsiElement,
project: Project,
daoMethod: PsiMethod?,
): PsiParentClass? {
var result: PsiParentClass? = null
var fieldAccessTopParentClass: PsiParentClass? = null
Expand Down Expand Up @@ -250,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
Expand Down Expand Up @@ -337,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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,9 @@ class SqlParameterCompletionProvider : CompletionProvider<CompletionParameters>(
): 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading