Skip to content

Commit

Permalink
Revert "Revert "Rename named argument usage on column rename (#4027)" (
Browse files Browse the repository at this point in the history
…#4051)" (#4077)

This reverts commit d19e01c.
  • Loading branch information
aperfilyev committed Apr 20, 2023
1 parent a40ae74 commit cd84fc7
Show file tree
Hide file tree
Showing 21 changed files with 266 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package app.cash.sqldelight.intellij

import com.alecstrong.sql.psi.core.psi.SqlColumnName
import com.intellij.find.findUsages.FindUsagesOptions
import com.intellij.openapi.application.ReadActionProcessor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.search.SearchScope
import com.intellij.refactoring.rename.RenamePsiElementProcessor
import com.intellij.usageView.UsageInfo
import com.intellij.util.CommonProcessors

class SqlDelightColumnRenameProcessor : RenamePsiElementProcessor() {

private val findUsagesHandlerFactory = SqlDelightFindUsagesHandlerFactory()
override fun canProcessElement(element: PsiElement): Boolean {
return element is SqlColumnName && findUsagesHandlerFactory.canFindUsages(element)
}

override fun findReferences(
element: PsiElement,
searchScope: SearchScope,
searchInCommentsAndStrings: Boolean,
): Collection<PsiReference> {
if (element !is SqlColumnName) {
return super.findReferences(element, searchScope, searchInCommentsAndStrings)
}

val collectProcessor = CommonProcessors.CollectProcessor<UsageInfo>()
val readActionProcessor = ReadActionProcessor.wrapInReadAction(collectProcessor)
val findUsagesHandler = findUsagesHandlerFactory.createFindUsagesHandler(element, false)
findUsagesHandler.processElementUsages(
/* element = */ element,
/* processor = */ readActionProcessor,
/* options = */ FindUsagesOptions(element.project).apply {
isUsages = true
isSearchForTextOccurrences = false
},
)
return collectProcessor.results.mapNotNull { it.reference }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import app.cash.sqldelight.core.lang.SqlDelightFile
import app.cash.sqldelight.core.lang.psi.StmtIdentifierMixin
import app.cash.sqldelight.core.lang.queriesName
import app.cash.sqldelight.core.psi.SqlDelightStmtIdentifier
import app.cash.sqldelight.core.psi.SqlDelightStmtList
import app.cash.sqldelight.intellij.usages.ReflectiveKotlinFindUsagesFactory
import com.alecstrong.sql.psi.core.psi.SqlColumnName
import com.alecstrong.sql.psi.core.psi.SqlStmt
import com.intellij.find.findUsages.FindUsagesHandler
import com.intellij.find.findUsages.FindUsagesHandlerFactory
import com.intellij.find.findUsages.FindUsagesOptions
Expand All @@ -19,14 +21,17 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.parentOfType
import com.intellij.usageView.UsageInfo
import com.intellij.util.Processor
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.getValueParameters

class SqlDelightFindUsagesHandlerFactory : FindUsagesHandlerFactory() {
internal class SqlDelightFindUsagesHandlerFactory : FindUsagesHandlerFactory() {
override fun canFindUsages(element: PsiElement): Boolean {
val module = ModuleUtil.findModuleForPsiElement(element) ?: return false
val isConfigured = SqlDelightFileIndex.getInstance(module).isConfigured
Expand All @@ -39,7 +44,7 @@ class SqlDelightFindUsagesHandlerFactory : FindUsagesHandlerFactory() {
): FindUsagesHandler = SqlDelightIdentifierHandler(element)
}

private class SqlDelightIdentifierHandler(
internal class SqlDelightIdentifierHandler(
private val element: PsiElement,
) : FindUsagesHandler(element) {
private val factory = ReflectiveKotlinFindUsagesFactory(element.project)
Expand Down Expand Up @@ -128,7 +133,19 @@ internal fun PsiElement.generatedKtFiles(): List<KtFile> {
}

internal fun SqlColumnName.generatedProperties(): Collection<KtNamedDeclaration> {
return generatedKtFiles()
val identifierList = parentOfType<SqlDelightStmtList>()?.stmtIdentifierList.orEmpty()
val namedStmts = identifierList.associateBy { it.getNextSiblingIgnoringWhitespaceAndComments() as? SqlStmt }
val stmtsWithColumn = namedStmts.keys.filterNotNull()
.filter { stmt ->
PsiTreeUtil.findChildrenOfType(stmt, SqlColumnName::class.java).any { it.textMatches(name) }
}

val generatedMethodParameters = stmtsWithColumn.mapNotNull { namedStmts[it] as? StmtIdentifierMixin }
.flatMap { it.generatedMethods() }
.flatMap { it.getValueParameters() }
.filter { it.name == name }

return generatedMethodParameters + generatedKtFiles()
.flatMap { it.declarations.filterIsInstance<KtClass>() }
.filter(KtClass::isData)
.flatMap(KtClass::getPrimaryConstructorParameters)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,75 +1,69 @@
package app.cash.sqldelight.intellij.inspections

import app.cash.sqldelight.core.lang.psi.StmtIdentifierMixin
import app.cash.sqldelight.core.lang.queriesName
import app.cash.sqldelight.core.lang.util.findChildOfType
import app.cash.sqldelight.core.psi.SqlDelightStmtIdentifier
import app.cash.sqldelight.core.psi.SqlDelightVisitor
import app.cash.sqldelight.intellij.SqlDelightFindUsagesHandlerFactory
import com.alecstrong.sql.psi.core.psi.SqlStmtList
import com.alecstrong.sql.psi.core.psi.SqlTypes
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.LocalQuickFixOnPsiElement
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.find.findUsages.FindUsagesOptions
import com.intellij.openapi.application.ReadActionProcessor
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.ReadOnlyModificationException
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.SmartPointerManager
import com.intellij.psi.search.FilenameIndex
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.psi.KtFile
import com.intellij.usageView.UsageInfo
import com.intellij.util.CommonProcessors

internal class UnusedQueryInspection : LocalInspectionTool() {

private val findUsagesFactory = SqlDelightFindUsagesHandlerFactory()
override fun buildVisitor(
holder: ProblemsHolder,
isOnTheFly: Boolean,
session: LocalInspectionToolSession,
) = ensureReady(session.file) {
val fileName = "${sqlDelightFile.virtualFile?.queriesName}.kt"

val virtualFile = FilenameIndex.getVirtualFilesByName(fileName, GlobalSearchScope.moduleScope(module))
.firstOrNull() ?: return PsiElementVisitor.EMPTY_VISITOR
val generatedFile = PsiManager.getInstance(sqlDelightFile.project).findFile(virtualFile) as KtFile?
?: return PsiElementVisitor.EMPTY_VISITOR

val allMethods = generatedFile.classes.firstOrNull()?.methods

if (allMethods == null) {
return PsiElementVisitor.EMPTY_VISITOR
}

return object : SqlDelightVisitor() {
override fun visitStmtIdentifier(o: SqlDelightStmtIdentifier) = ignoreInvalidElements {
if (o !is StmtIdentifierMixin || o.identifier() == null) {
return
}
val generatedMethods = allMethods.filter { namedFunction ->
namedFunction.name == o.identifier()?.text
}
for (generatedMethod in generatedMethods) {
val lightMethods = generatedMethod.toLightMethods()
if (lightMethods.any { ReferencesSearch.search(it, it.useScope).findFirst() != null }) {
return
): PsiElementVisitor {
return ensureReady(session.file) {
object : SqlDelightVisitor() {
override fun visitStmtIdentifier(o: SqlDelightStmtIdentifier) {
ignoreInvalidElements {
if (o !is StmtIdentifierMixin || o.identifier() == null) {
return
}
if (!hasUsages(o)) {
holder.registerProblem(o, "Unused symbol", ProblemHighlightType.LIKE_UNUSED_SYMBOL, SafeDeleteQuickFix(o))
}
}
}
holder.registerProblem(
o,
"Unused symbol",
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
SafeDeleteQuickFix(o),
)
}
}
}

private fun hasUsages(stmtIdentifier: SqlDelightStmtIdentifier): Boolean {
if (!findUsagesFactory.canFindUsages(stmtIdentifier)) {
return false
}
val findFirstProcessor = CommonProcessors.FindFirstProcessor<UsageInfo>()
val readActionProcessor = ReadActionProcessor.wrapInReadAction(findFirstProcessor)
val findUsagesOptions = FindUsagesOptions(stmtIdentifier.project).apply {
isUsages = true
isSearchForTextOccurrences = false
}
val findUsagesHandler = findUsagesFactory.createFindUsagesHandler(stmtIdentifier, true)
findUsagesHandler.processElementUsages(stmtIdentifier, readActionProcessor, findUsagesOptions)
return findFirstProcessor.isFound
}

class SafeDeleteQuickFix(element: PsiElement) : LocalQuickFixOnPsiElement(element) {
private val ref = SmartPointerManager.getInstance(element.project)
.createSmartPsiElementPointer(element, element.containingFile)
Expand Down
3 changes: 3 additions & 0 deletions sqldelight-idea-plugin/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@
implementation="app.cash.sqldelight.intellij.SqlDelightFindUsagesHandlerFactory"/>
<renamePsiElementProcessor
implementation="app.cash.sqldelight.intellij.SqlDelightRenameProcessor"/>
<renamePsiElementProcessor
implementation="app.cash.sqldelight.intellij.SqlDelightColumnRenameProcessor"/>
<psi.referenceContributor
language="SqlDelight"
implementation="app.cash.sqldelight.intellij.SqlDelightReferenceContributor"/>

<lang.findUsagesProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance

class FindUsagesTest : SqlDelightProjectTestCase() {
// With the kotlin 1.3.30 plugin the test breaks. Find usages still works. Investigate later.
fun ignoretestFindsBothKotlinAndJavaUsages() {

fun testFindsBothKotlinAndJavaUsages() {
myFixture.openFileInEditor(
tempRoot.findFileByRelativePath("src/main/java/com/example/SampleClass.java")!!,
)
Expand All @@ -40,7 +40,7 @@ class FindUsagesTest : SqlDelightProjectTestCase() {
}

// With the kotlin 1.3.30 plugin the test breaks. Find usages still works. Investigate later.
fun ignoretestFindsUsagesOfAllGeneratedMethods() {
fun testFindsUsagesOfAllGeneratedMethods() {
myFixture.openFileInEditor(
tempRoot.findFileByRelativePath("src/main/kotlin/com/example/KotlinClass.kt")!!,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package app.cash.sqldelight.intellij

import com.alecstrong.sql.psi.core.psi.SqlColumnName

class SqlDelightColumnRenameProcessorTest : SqlDelightFixtureTestCase() {

override val fixtureDirectory: String = "find-usages"

fun testColumnRename() {
myFixture.configureByFile("Example.sq")
myFixture.copyFileToProject("Main.kt", "main/kotlin/com/example/Main.kt")
myFixture.copyFileToProject("Example.kt", "build/com/example/Example.kt")
myFixture.copyFileToProject("ExampleQueries.kt", "build/com/example/ExampleQueries.kt")
myFixture.copyFileToProject("Query.kt", "build/com/example/Query.kt")
val sqlColumnName = myFixture.findElementByText("id", SqlColumnName::class.java)

myFixture.renameElement(sqlColumnName, "newId")

myFixture.checkResultByFile("main/kotlin/com/example/Main.kt", "MainAfter.kt", false)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ abstract class SqlDelightFixtureTestCase : LightJavaCodeInsightFixtureTestCase()
override val deriveSchemaFromMigrations = false

override fun outputDirectory(file: SqlDelightFile) = outputDirectories()
override fun outputDirectories() = listOf("")
override fun outputDirectories() = listOf("build")

override fun sourceFolders(
file: SqlDelightFile,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package app.cash.sqldelight.intellij

import com.alecstrong.sql.psi.core.psi.SqlColumnName
import com.google.common.truth.Truth.assertThat

class SqlDelightIdentifierHandlerTest : SqlDelightFixtureTestCase() {

override val fixtureDirectory: String = "find-usages"

fun testFindColumnUsages() {
myFixture.configureByFile("Example.sq")
myFixture.copyFileToProject("Main.kt", "main/kotlin/com/example/Main.kt")
myFixture.copyFileToProject("Example.kt", "build/com/example/Example.kt")
myFixture.copyFileToProject("ExampleQueries.kt", "build/com/example/ExampleQueries.kt")
myFixture.copyFileToProject("Query.kt", "build/com/example/Query.kt")
val sqlColumnName = myFixture.findElementByText("id", SqlColumnName::class.java)

val usageInfos = myFixture.findUsages(sqlColumnName)

assertThat(usageInfos.size).isEqualTo(6)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package app.cash.sqldelight.intellij.inspections

import app.cash.sqldelight.intellij.SqlDelightFixtureTestCase
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper

class UnusedQueryInspectionTest : SqlDelightFixtureTestCase() {

override val fixtureDirectory: String = "unused-query-inspection"

fun testInspection() {
myFixture.copyFileToProject("Main.kt", "main/kotlin/com/example/Main.kt")
myFixture.copyFileToProject("SomeTableQueries.kt", "build/com/example/SomeTableQueries.kt")
myFixture.copyFileToProject("Example.kt", "build/com/example/Example.kt")
myFixture.copyFileToProject("Query.kt", "build/com/example/Query.kt")

myFixture.testInspection("", LocalInspectionToolWrapper(UnusedQueryInspection()))
}
}
9 changes: 9 additions & 0 deletions sqldelight-idea-plugin/testData/find-usages/Example.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example

import kotlin.Long
import kotlin.String

public data class Example(
public val id: Long,
public val name: String,
)
10 changes: 10 additions & 0 deletions sqldelight-idea-plugin/testData/find-usages/Example.sq
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE TABLE example (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL
);

selectAll:
SELECT id, name FROM example;

selectById:
SELECT id, name FROM example WHERE id = ?;
11 changes: 11 additions & 0 deletions sqldelight-idea-plugin/testData/find-usages/ExampleQueries.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example

import Query
import kotlin.Long

public class ExampleQueries() {

public fun selectById(id: Long): Query<Example> {
return Query(Example(1, "foo"))
}
}
8 changes: 8 additions & 0 deletions sqldelight-idea-plugin/testData/find-usages/Main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ktlint-disable filename
package com.example

fun main() {
val exampleQueries = ExampleQueries()
val query = exampleQueries.selectById(id = 1L)
val id = query.executeAsOneOrNull()?.id
}
8 changes: 8 additions & 0 deletions sqldelight-idea-plugin/testData/find-usages/MainAfter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ktlint-disable filename
package com.example

fun main() {
val exampleQueries = ExampleQueries()
val query = exampleQueries.selectById(newId = 1L)
val id = query.executeAsOneOrNull()?.newId
}
5 changes: 5 additions & 0 deletions sqldelight-idea-plugin/testData/find-usages/Query.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Query<T>(val value: T) {
fun executeAsOneOrNull(): T {
return value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example

import kotlin.Long
import kotlin.String

public data class Example(
public val id: Long,
public val name: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example

fun main() {
val someTableQueries = SomeTableQueries()
someTableQueries.selectAll()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Query<T>(val value: T) {
fun executeAsOneOrNull(): T {
return value
}
}
Loading

0 comments on commit cd84fc7

Please sign in to comment.