Skip to content

Commit 6bc0691

Browse files
authored
[IJ plugin] v3 -> v4 migration: enum capitalization (#5128)
1 parent 1ff7212 commit 6bc0691

File tree

9 files changed

+173
-4
lines changed

9 files changed

+173
-4
lines changed

intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/refactoring/Refactoring.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.intellij.psi.PsiMigration
1212
import com.intellij.psi.PsiPackage
1313
import com.intellij.psi.PsiReference
1414
import com.intellij.psi.search.GlobalSearchScope
15+
import com.intellij.psi.search.searches.AllClassesSearch
1516
import com.intellij.psi.search.searches.ClassInheritorsSearch
1617
import com.intellij.psi.search.searches.ReferencesSearch
1718
import com.intellij.refactoring.rename.RenamePsiElementProcessor
@@ -96,3 +97,7 @@ fun findInheritorsOfClass(project: Project, className: String): Collection<PsiCl
9697
// Using allScope for the search so all inheritors are found even if some of them are not in the project
9798
return ClassInheritorsSearch.search(psiLookupClass, GlobalSearchScope.allScope(project), true).toList()
9899
}
100+
101+
fun findAllClasses(project: Project): Collection<PsiClass> {
102+
return AllClassesSearch.search(GlobalSearchScope.projectScope(project), project).findAll()
103+
}

intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/refactoring/migration/ApolloMigrationRefactoringProcessor.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,12 @@ abstract class ApolloMigrationRefactoringProcessor(project: Project) : BaseRefac
8484
val usageInfos = migrationItems
8585
.flatMap { migrationItem ->
8686
migrationItem.findUsages(myProject, migration!!, searchScope)
87-
.filterNot { usageInfo ->
87+
.filter { usageInfo ->
8888
// Filter out all generated code usages. We don't want generated code to come up in findUsages.
89-
// TODO: how to mark Apollo generated code as generated per this method?
90-
usageInfo.virtualFile?.isGenerated(myProject) == true
89+
usageInfo.virtualFile?.isGenerated(myProject) != true //&&
90+
91+
// Also filter out usages outside of projects (see https://youtrack.jetbrains.com/issue/KTIJ-26411)
92+
usageInfo.virtualFile?.let { searchScope.contains(it) } == true
9193
}
9294
}
9395
.toMutableList()

intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/refactoring/migration/v3tov4/ApolloV3ToV4MigrationProcessor.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import com.apollographql.ijplugin.refactoring.migration.item.UpdateGradlePluginI
1414
import com.apollographql.ijplugin.refactoring.migration.item.UpdateMethodCall
1515
import com.apollographql.ijplugin.refactoring.migration.item.UpdateMethodName
1616
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.EncloseInService
17+
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.RemoveFieldInService
1718
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.RemoveWatchMethodArguments
19+
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.UpdateEnumClassUpperCase
1820
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.UpdateFieldNameInService
1921
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.UpdateMultiModuleConfiguration
2022
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.UpdateWebSocketReconnectWhen
@@ -73,13 +75,16 @@ class ApolloV3ToV4MigrationProcessor(project: Project) : ApolloMigrationRefactor
7375
ConstructorInsteadOfBuilder("$apollo3.cache.normalized.api.CacheKey.Companion", "from"),
7476
UpdateWebSocketReconnectWhen,
7577

78+
UpdateEnumClassUpperCase,
79+
7680
// Gradle
7781
UpdateGradlePluginInBuildKts(apollo3, apollo3, apollo4LatestVersion),
7882
UpdateGradleDependenciesInToml(apollo3, apollo3, apollo4LatestVersion),
7983
UpdateGradleDependenciesBuildKts(apollo3, apollo3),
8084

8185
UpdateFieldNameInService("generateModelBuilder", "generateModelBuilders"),
8286
UpdateFieldNameInService("generateTestBuilders", "generateDataBuilders"),
87+
RemoveFieldInService("languageVersion"),
8388
UpdateCustomTypeMappingInBuildKts,
8489
UpdateMultiModuleConfiguration,
8590
EncloseInService,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.apollographql.ijplugin.refactoring.migration.v3tov4.item
2+
3+
import com.apollographql.ijplugin.refactoring.migration.item.DeletesElements
4+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItem
5+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItemUsageInfo
6+
import com.apollographql.ijplugin.refactoring.migration.item.toMigrationItemUsageInfo
7+
import com.apollographql.ijplugin.util.cast
8+
import com.apollographql.ijplugin.util.findPsiFilesByName
9+
import com.apollographql.ijplugin.util.getMethodName
10+
import com.intellij.openapi.project.Project
11+
import com.intellij.psi.PsiMigration
12+
import com.intellij.psi.search.GlobalSearchScope
13+
import org.jetbrains.kotlin.psi.KtCallExpression
14+
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
15+
import org.jetbrains.kotlin.psi.KtFile
16+
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
17+
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
18+
19+
class RemoveFieldInService(
20+
val fieldName:String,
21+
) : MigrationItem(), DeletesElements {
22+
override fun findUsages(project: Project, migration: PsiMigration, searchScope: GlobalSearchScope): List<MigrationItemUsageInfo> {
23+
val usages = mutableListOf<MigrationItemUsageInfo>()
24+
val buildGradleKtsFiles: List<KtFile> = project.findPsiFilesByName("build.gradle.kts", searchScope).filterIsInstance<KtFile>()
25+
for (file in buildGradleKtsFiles) {
26+
file.accept(object : KtTreeVisitorVoid() {
27+
override fun visitCallExpression(expression: KtCallExpression) {
28+
super.visitCallExpression(expression)
29+
if (expression.getMethodName() == "apollo") {
30+
expression.accept(object : KtTreeVisitorVoid() {
31+
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
32+
super.visitDotQualifiedExpression(expression)
33+
if (expression.receiverExpression.cast<KtNameReferenceExpression>()?.getReferencedName() == fieldName) {
34+
usages.add(expression.toMigrationItemUsageInfo())
35+
}
36+
}
37+
})
38+
}
39+
}
40+
})
41+
}
42+
return usages
43+
}
44+
45+
override fun performRefactoring(project: Project, migration: PsiMigration, usage: MigrationItemUsageInfo) {
46+
val element = usage.element
47+
element.delete()
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.apollographql.ijplugin.refactoring.migration.v3tov4.item
2+
3+
import com.apollographql.ijplugin.navigation.isApolloEnumClass
4+
import com.apollographql.ijplugin.refactoring.findAllClasses
5+
import com.apollographql.ijplugin.refactoring.findClassReferences
6+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItem
7+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItemUsageInfo
8+
import com.apollographql.ijplugin.refactoring.migration.item.toMigrationItemUsageInfo
9+
import com.apollographql.ijplugin.util.capitalizeFirstLetter
10+
import com.apollographql.ijplugin.util.cast
11+
import com.apollographql.ijplugin.util.isGenerated
12+
import com.intellij.openapi.project.Project
13+
import com.intellij.psi.PsiMigration
14+
import com.intellij.psi.search.GlobalSearchScope
15+
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
16+
import org.jetbrains.kotlin.psi.KtClass
17+
import org.jetbrains.kotlin.psi.KtPsiFactory
18+
19+
object UpdateEnumClassUpperCase : MigrationItem() {
20+
override fun findUsages(project: Project, migration: PsiMigration, searchScope: GlobalSearchScope): List<MigrationItemUsageInfo> {
21+
val allClasses = findAllClasses(project)
22+
val allApolloGeneratedEnums: List<KtClass> = allClasses
23+
.filter { it.isEnum }
24+
.mapNotNull { it.cast<KtUltraLightClass>()?.kotlinOrigin?.cast<KtClass>() }
25+
.filter { it.isApolloEnumClass() && it.name!![0].isLowerCase() }
26+
return allApolloGeneratedEnums.flatMap {
27+
findClassReferences(project, it.fqName!!.asString())
28+
// Exclude references in generated code
29+
.filterNot { it.element.containingFile?.virtualFile?.isGenerated(project) == true }
30+
31+
}.toMigrationItemUsageInfo()
32+
}
33+
34+
override fun performRefactoring(project: Project, migration: PsiMigration, usage: MigrationItemUsageInfo) {
35+
usage.element.replace(KtPsiFactory(project).createExpression(usage.element.text.capitalizeFirstLetter()))
36+
}
37+
}

intellij-plugin/src/test/kotlin/com/apollographql/ijplugin/ApolloV3ToV4MigrationTest.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class ApolloV3ToV4MigrationTest : ApolloTestCase() {
3131
fun testUpdateGradleDependenciesInLibsVersionsToml() = runMigration(extension = "versions.toml", fileNameInProject = "libs.versions.toml")
3232

3333
@Test
34-
fun deprecations() = runMigration()
34+
fun testDeprecations() = runMigration()
3535

3636
@Test
3737
fun testEncloseInService() = runMigration(extension = "gradle.kts", fileNameInProject = "build.gradle.kts")
@@ -45,6 +45,9 @@ class ApolloV3ToV4MigrationTest : ApolloTestCase() {
4545
@Test
4646
fun testMultiModule() = runMigration(extension = "gradle.kts", fileNameInProject = "build.gradle.kts")
4747

48+
@Test
49+
fun testUpdateEnumClassUpperCase() = runMigration()
50+
4851
private fun runMigration(extension: String = "kt", fileNameInProject: String? = null) {
4952
val fileBaseName = getTestName(true)
5053
if (fileNameInProject != null) {

intellij-plugin/src/test/testData/migration/v3-to-v4/gradleDeprecations.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ dependencies {
55
apollo {
66
generateModelBuilder.set(true)
77
generateTestBuilders.set(true)
8+
languageVersion.set("1.4")
89
}
910

1011
apollo {
1112
service("xxx") {
1213
generateModelBuilder.set(true)
1314
generateTestBuilders.set(true)
15+
languageVersion.set("1.4")
1416
}
1517
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.example
2+
3+
import com.apollographql.apollo3.api.EnumType
4+
5+
public enum class myEnum(
6+
public val rawValue: String,
7+
) {
8+
VALUE_A("VALUE_A"),
9+
VALUE_B("VALUE_B"),
10+
11+
/**
12+
* Auto generated constant for unknown enum values
13+
*/
14+
UNKNOWN__("UNKNOWN__"),
15+
;
16+
17+
public companion object {
18+
public val type: EnumType = EnumType("myEnum", listOf("VALUE_A", "VALUE_B"))
19+
20+
public fun safeValueOf(rawValue: String): myEnum = values().find { it.rawValue == rawValue } ?: UNKNOWN__
21+
22+
/**
23+
* Returns all [myEnum] known at compile time
24+
*/
25+
public fun knownValues(): Array<myEnum> = arrayOf(
26+
VALUE_A,
27+
VALUE_B)
28+
}
29+
}
30+
31+
fun main() {
32+
val myEnum: myEnum = myEnum.VALUE_A
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.example
2+
3+
import com.apollographql.apollo3.api.EnumType
4+
5+
public enum class myEnum(
6+
public val rawValue: String,
7+
) {
8+
VALUE_A("VALUE_A"),
9+
VALUE_B("VALUE_B"),
10+
11+
/**
12+
* Auto generated constant for unknown enum values
13+
*/
14+
UNKNOWN__("UNKNOWN__"),
15+
;
16+
17+
public companion object {
18+
public val type: EnumType = EnumType("myEnum", listOf("VALUE_A", "VALUE_B"))
19+
20+
public fun safeValueOf(rawValue: String): MyEnum = values().find { it.rawValue == rawValue } ?: UNKNOWN__
21+
22+
/**
23+
* Returns all [MyEnum] known at compile time
24+
*/
25+
public fun knownValues(): Array<MyEnum> = arrayOf(
26+
VALUE_A,
27+
VALUE_B)
28+
}
29+
}
30+
31+
fun main() {
32+
val myEnum: MyEnum = MyEnum.VALUE_A
33+
}

0 commit comments

Comments
 (0)