Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move single-use FIR annotation matching #454

Merged
merged 1 commit into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Expand Up @@ -107,7 +107,6 @@ class PokoBuildPlugin : Plugin<Project> {
buildConfigField("COMPILER_PLUGIN_ARTIFACT", "poko-compiler-plugin")
buildConfigField("DEFAULT_POKO_ENABLED", true)
buildConfigField("DEFAULT_POKO_ANNOTATION", "dev/drewhamilton/poko/Poko")
buildConfigField("SKIP_ANNOTATION_SHORT_NAME", "Skip")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.drewhamilton.poko

import org.jetbrains.kotlin.name.Name

internal object PokoAnnotationNames {
val ReadArrayContent = Name.identifier("ReadArrayContent")
val Skip = Name.identifier("Skip")
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChecker
import org.jetbrains.kotlin.fir.analysis.checkers.hasModifier
import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.primaryConstructorIfAny
Expand All @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneTypeOrNull
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.ClassId

internal class PokoFirCheckersExtension(
session: FirSession,
Expand All @@ -46,8 +48,8 @@ internal class PokoFirCheckersExtension(
context: CheckerContext,
reporter: DiagnosticReporter,
) {
val matcher = context.session.pokoFirExtensionSessionComponent
if (matcher.pokoAnnotation(declaration) == null) return
val sessionComponent = context.session.pokoFirExtensionSessionComponent
if (!declaration.hasAnnotation(sessionComponent.pokoAnnotation)) return

val errorFactory = when {
declaration.classKind != ClassKind.CLASS -> Diagnostics.PokoOnNonClass
Expand All @@ -72,19 +74,17 @@ internal class PokoFirCheckersExtension(
it.source?.kind is KtFakeSourceElementKind.PropertyFromParameter
}
.filter {
val skipAnnotation = matcher.pokoSkipAnnotation(it)
if (
skipAnnotation != null &&
!skipAnnotation.isNestedInDefaultPokoAnnotation()
) {
val skipAnnotation = sessionComponent.pokoSkipAnnotation
val hasSkipAnnotation = it.hasAnnotation(skipAnnotation)
if (hasSkipAnnotation && !skipAnnotation.isNestedInDefaultPokoAnnotation()) {
// Pseudo-opt-in warning for custom annotation consumers:
reporter.reportOn(
source = it.source,
factory = Diagnostics.SkippedPropertyWithCustomAnnotation,
context = context,
)
}
skipAnnotation == null
!hasSkipAnnotation
}
if (constructorProperties.isEmpty()) {
reporter.reportOn(
Expand All @@ -95,13 +95,23 @@ internal class PokoFirCheckersExtension(
}
}

private fun FirAnnotation.isNestedInDefaultPokoAnnotation(): Boolean {
val outerFqName = annotationTypeRef.coneTypeOrNull!!.classId!!
.outerClassId!!
.asFqNameString()
private fun FirDeclaration.hasAnnotation(
annotation: ClassId,
): Boolean {
return annotations.any { firAnnotation ->
firAnnotation.classId() == annotation
}
}

private fun FirAnnotation.classId(): ClassId? {
return annotationTypeRef.coneTypeOrNull?.classId
}

private fun ClassId.isNestedInDefaultPokoAnnotation(): Boolean {
val outerFqName = outerClassId?.asFqNameString()
return outerFqName == DEFAULT_POKO_ANNOTATION ||
// Multiplatform FqName has "." instead of "/" for package:
outerFqName.replace(".", "/") == DEFAULT_POKO_ANNOTATION
outerFqName?.replace(".", "/") == DEFAULT_POKO_ANNOTATION
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,18 @@
package dev.drewhamilton.poko.fir

import dev.drewhamilton.poko.BuildConfig.SKIP_ANNOTATION_SHORT_NAME
import dev.drewhamilton.poko.PokoAnnotationNames
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent.Factory
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneTypeOrNull
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name

internal class PokoFirExtensionSessionComponent(
session: FirSession,
private val pokoAnnotation: ClassId,
internal val pokoAnnotation: ClassId,
) : FirExtensionSessionComponent(session) {
fun pokoAnnotation(declaration: FirDeclaration): FirAnnotation? {
return declaration.annotations.firstOrNull { firAnnotation ->
firAnnotation.classId() == pokoAnnotation
}
}

fun pokoSkipAnnotation(declaration: FirDeclaration): FirAnnotation? {
val skipAnnotation = pokoAnnotation.createNestedClassId(
name = Name.identifier(SKIP_ANNOTATION_SHORT_NAME),
)
return declaration.annotations.firstOrNull { firAnnotation ->
firAnnotation.classId() == skipAnnotation
}
}

private fun FirAnnotation.classId(): ClassId? {
return annotationTypeRef.coneTypeOrNull?.classId
}
internal val pokoSkipAnnotation: ClassId =
pokoAnnotation.createNestedClassId(PokoAnnotationNames.Skip)

internal companion object {
internal fun getFactory(pokoAnnotation: ClassId): Factory {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.drewhamilton.poko.ir

import dev.drewhamilton.poko.BuildConfig.SKIP_ANNOTATION_SHORT_NAME
import dev.drewhamilton.poko.PokoAnnotationNames
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
Expand Down Expand Up @@ -134,9 +134,7 @@ internal class PokoMembersTransformer(
}
.filter {
!it.hasAnnotation(
classId = ClassId.fromString(
string = "${pokoAnnotationName}.$SKIP_ANNOTATION_SHORT_NAME"
),
classId = pokoAnnotationName.createNestedClassId(PokoAnnotationNames.Skip),
)
}
if (properties.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.drewhamilton.poko.ir

import dev.drewhamilton.poko.PokoAnnotationNames
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.ir.builders.IrBlockBodyBuilder
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextInterface
Expand All @@ -25,7 +26,6 @@ import org.jetbrains.kotlin.ir.util.isAnnotationClass
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name

/**
* The type of an [IrProperty].
Expand Down Expand Up @@ -62,7 +62,7 @@ internal fun IrBlockBodyBuilder.IrGetValueImpl(
internal fun IrProperty.hasReadArrayContentAnnotation(
pokoAnnotation: ClassId,
): Boolean = hasAnnotation(
classId = pokoAnnotation.createNestedClassId(Name.identifier("ReadArrayContent")),
classId = pokoAnnotation.createNestedClassId(PokoAnnotationNames.ReadArrayContent),
)

/**
Expand Down
Loading