From c4f931c044008a101e110f226fc799274f7492cb Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Fri, 20 Dec 2024 21:03:31 -0500 Subject: [PATCH 1/7] Support multiple custom annotations --- CHANGELOG.md | 3 + README.md | 14 ++-- .../gradle/RedactedGradlePluginExtension.kt | 28 ++++++- .../gradle/RedactedGradleSubplugin.kt | 29 +++++-- .../compiler/RedactedCommandLineProcessor.kt | 24 +++--- .../compiler/RedactedIrGenerationExtension.kt | 9 +- .../redacted/compiler/RedactedIrVisitor.kt | 15 ++-- .../redacted/compiler/RedactedPlugin.kt | 17 ++-- .../fir/FirRedactedExtensionRegistrar.kt | 82 +++++++++++-------- .../compiler/fir/RedactedFirBuiltIns.kt | 16 ++-- .../dev/zacsweers/redacted/compiler/util.kt | 8 ++ .../redacted/compiler/RedactedPluginTest.kt | 8 +- 12 files changed, 161 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5410f8c3..dd6fa9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ Changelog - Fix FIR diagnostics rendering in the IDE. Note this only works in the K2 Kotlin IDE plugin + setting the IntelliJ `kotlin.k2.only.bundled.compiler.plugins.enabled` registry key to `false`. - When custom annotations are defined, report those names in FIR error messages. +- Support multiple custom annotations. + - For Gradle configuration, the singular `*Annotation` properties are deprecated in favor of plural`*Annotations` `SetProperty` types. + - For CLI consumers, the `redactedAnnotation` and `unredactedAnnotation` properties are now `redactedAnnotations` and `unredactedAnnotations`. - Build against Gradle `8.12`. - Only report errors in FIR now. Removes the `validateIr` plugin option. - No longer support K1. diff --git a/README.md b/README.md index cf253e08..e206163e 100644 --- a/README.md +++ b/README.md @@ -57,17 +57,17 @@ You can configure custom behavior with properties on the `redacted` extension. ```kotlin redacted { - // Define a custom annotation. The -annotations artifact won't be automatically added to + // Define custom annotations. The -annotations artifact won't be automatically added to // dependencies if you define your own! - // Note that this must be in the format of a string where packages are delimited by '/' and + // Note that these must be in the format of a string where packages are delimited by '/' and // classes by '.', e.g. "kotlin/Map.Entry" - redactedAnnotation = "dev/zacsweers/redacted/annotations/Redacted" // Default + redactedAnnotations.add("dev/zacsweers/redacted/annotations/Redacted") // Default - // Define a custom unredacted annotation. - unredactedAnnotation = "dev/zacsweers/redacted/annotations/Unredacted" // Default + // Define custom unredacted annotations. + unredactedAnnotations.add("dev/zacsweers/redacted/annotations/Unredacted") // Default - // Define whether or not this is enabled. Useful if you want to gate this behind a dynamic - // build configuration. + // Define whether this plugin is enabled on this compilation. Useful if you want to + // gate this behind a dynamic build configuration. enabled = true // Default // Define a custom replacement string for redactions. diff --git a/redacted-compiler-plugin-gradle/src/main/kotlin/dev/zacsweers/redacted/gradle/RedactedGradlePluginExtension.kt b/redacted-compiler-plugin-gradle/src/main/kotlin/dev/zacsweers/redacted/gradle/RedactedGradlePluginExtension.kt index f0ec2eda..c55a8384 100644 --- a/redacted-compiler-plugin-gradle/src/main/kotlin/dev/zacsweers/redacted/gradle/RedactedGradlePluginExtension.kt +++ b/redacted-compiler-plugin-gradle/src/main/kotlin/dev/zacsweers/redacted/gradle/RedactedGradlePluginExtension.kt @@ -18,27 +18,47 @@ package dev.zacsweers.redacted.gradle import javax.inject.Inject import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty internal const val DEFAULT_ANNOTATION = "dev/zacsweers/redacted/annotations/Redacted" +internal val DEFAULT_ANNOTATION_SET = setOf(DEFAULT_ANNOTATION) internal const val DEFAULT_UNREDACTED_ANNOTATION = "dev/zacsweers/redacted/annotations/Unredacted" +internal val DEFAULT_UNREDACTED_ANNOTATION_SET = setOf(DEFAULT_UNREDACTED_ANNOTATION) public abstract class RedactedPluginExtension @Inject constructor(objects: ObjectFactory) { + @Deprecated("Use redactedAnnotations instead", ReplaceWith("redactedAnnotations")) + public val redactedAnnotation: Property = + objects.property(String::class.java).convention(DEFAULT_ANNOTATION) + /** - * Define a custom redacted marker annotation. The -annotations artifact won't be automatically + * Define custom redacted marker annotations. The -annotations artifact won't be automatically * added to dependencies if you define your own! * - * Note that this must be in the format of a string where packages are delimited by '/' and + * Note that these must be in the format of a string where packages are delimited by '/' and * classes by '.', e.g. "kotlin/Map.Entry" */ - public val redactedAnnotation: Property = - objects.property(String::class.java).convention(DEFAULT_ANNOTATION) + public val redactedAnnotations: SetProperty = + objects.setProperty(String::class.java).convention(setOf(DEFAULT_ANNOTATION)) + @Deprecated("Use unredactedAnnotations instead", ReplaceWith("unredactedAnnotations")) public val unredactedAnnotation: Property = objects.property(String::class.java).convention(DEFAULT_UNREDACTED_ANNOTATION) + /** + * Define custom unredacted marker annotations. The -annotations artifact won't be automatically + * added to dependencies if you define your own! + * + * Note that these must be in the format of a string where packages are delimited by '/' and + * classes by '.', e.g. "kotlin/Map.Entry" + */ + public val unredactedAnnotations: SetProperty = + objects.setProperty(String::class.java).convention(setOf(DEFAULT_UNREDACTED_ANNOTATION)) + + /** Flag to enable/disable the plugin on this specific compilation. */ public val enabled: Property = objects.property(Boolean::class.javaObjectType).convention(true) + /** Defines a custom replacement string. The default is "██". */ public val replacementString: Property = objects.property(String::class.java).convention("██") } diff --git a/redacted-compiler-plugin-gradle/src/main/kotlin/dev/zacsweers/redacted/gradle/RedactedGradleSubplugin.kt b/redacted-compiler-plugin-gradle/src/main/kotlin/dev/zacsweers/redacted/gradle/RedactedGradleSubplugin.kt index 526c8fc9..33d828fa 100644 --- a/redacted-compiler-plugin-gradle/src/main/kotlin/dev/zacsweers/redacted/gradle/RedactedGradleSubplugin.kt +++ b/redacted-compiler-plugin-gradle/src/main/kotlin/dev/zacsweers/redacted/gradle/RedactedGradleSubplugin.kt @@ -44,12 +44,28 @@ public class RedactedGradleSubplugin : KotlinCompilerPluginSupportPlugin { ): Provider> { val project = kotlinCompilation.target.project val extension = project.extensions.getByType(RedactedPluginExtension::class.java) - val annotation = extension.redactedAnnotation - val unredactedAnnotation = extension.unredactedAnnotation + @Suppress("DEPRECATION") + val annotations = + extension.redactedAnnotations.zip(extension.redactedAnnotation) { + annotations, + singleAnnotation -> + annotations + singleAnnotation + } + @Suppress("DEPRECATION") + val unredactedAnnotations = + extension.unredactedAnnotations.zip(extension.unredactedAnnotation) { + annotations, + singleAnnotation -> + annotations + singleAnnotation + } // Default annotation is used, so add it as a dependency // Note only multiplatform, jvm/android, and js are supported. Anyone else is on their own. - if (annotation.get() == DEFAULT_ANNOTATION) { + val useDefaults = + annotations.getOrElse(DEFAULT_ANNOTATION_SET) == DEFAULT_ANNOTATION_SET || + unredactedAnnotations.getOrElse(DEFAULT_UNREDACTED_ANNOTATION_SET) == + DEFAULT_UNREDACTED_ANNOTATION_SET + if (useDefaults) { project.dependencies.add( kotlinCompilation.implementationConfigurationName, "dev.zacsweers.redacted:redacted-compiler-plugin-annotations:$VERSION", @@ -62,8 +78,11 @@ public class RedactedGradleSubplugin : KotlinCompilerPluginSupportPlugin { listOf( SubpluginOption(key = "enabled", value = enabled.toString()), SubpluginOption(key = "replacementString", value = extension.replacementString.get()), - SubpluginOption(key = "redactedAnnotation", value = annotation.get()), - SubpluginOption(key = "unredactedAnnotation", value = unredactedAnnotation.get()), + SubpluginOption(key = "redactedAnnotations", value = annotations.get().joinToString(",")), + SubpluginOption( + key = "unredactedAnnotations", + value = unredactedAnnotations.get().joinToString(","), + ), ) } } diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedCommandLineProcessor.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedCommandLineProcessor.kt index f470c112..b25ea3ea 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedCommandLineProcessor.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedCommandLineProcessor.kt @@ -27,13 +27,13 @@ internal val KEY_ENABLED = CompilerConfigurationKey("Enable/disable Redacted's plugin on the given compilation") internal val KEY_REPLACEMENT_STRING = CompilerConfigurationKey("The replacement string to use in redactions") -internal val KEY_REDACTED_ANNOTATION = +internal val KEY_REDACTED_ANNOTATIONS = CompilerConfigurationKey( - "The redacted marker annotation (i.e. com/example/Redacted) to look for when redacting" + "The redacted marker annotations (i.e. com/example/Redacted) to look for when redacting" ) internal val KEY_UNREDACTED_ANNOTATION = CompilerConfigurationKey( - "The unredacted marker annotation (i.e. com/example/Unredacted) to look for when redacting" + "The unredacted marker annotations (i.e. com/example/Unredacted) to look for when redacting" ) @OptIn(ExperimentalCompilerApi::class) @@ -59,18 +59,18 @@ public class RedactedCommandLineProcessor : CommandLineProcessor { allowMultipleOccurrences = false, ) - val OPTION_REDACTED_ANNOTATION = + val OPTION_REDACTED_ANNOTATIONS = CliOption( - optionName = "redactedAnnotation", + optionName = "redactedAnnotations", valueDescription = "String", - description = KEY_REDACTED_ANNOTATION.toString(), + description = KEY_REDACTED_ANNOTATIONS.toString(), required = true, allowMultipleOccurrences = false, ) - val OPTION_UNREDACTED_ANNOTATION = + val OPTION_UNREDACTED_ANNOTATIONS = CliOption( - optionName = "unredactedAnnotation", + optionName = "unredactedAnnotations", valueDescription = "String", description = KEY_UNREDACTED_ANNOTATION.toString(), required = true, @@ -84,8 +84,8 @@ public class RedactedCommandLineProcessor : CommandLineProcessor { listOf( OPTION_ENABLED, OPTION_REPLACEMENT_STRING, - OPTION_REDACTED_ANNOTATION, - OPTION_UNREDACTED_ANNOTATION, + OPTION_REDACTED_ANNOTATIONS, + OPTION_UNREDACTED_ANNOTATIONS, ) override fun processOption( @@ -96,8 +96,8 @@ public class RedactedCommandLineProcessor : CommandLineProcessor { when (option.optionName) { "enabled" -> configuration.put(KEY_ENABLED, value.toBoolean()) "replacementString" -> configuration.put(KEY_REPLACEMENT_STRING, value) - "redactedAnnotation" -> configuration.put(KEY_REDACTED_ANNOTATION, value) - "unredactedAnnotation" -> configuration.put(KEY_UNREDACTED_ANNOTATION, value) + "redactedAnnotations" -> configuration.put(KEY_REDACTED_ANNOTATIONS, value) + "unredactedAnnotations" -> configuration.put(KEY_UNREDACTED_ANNOTATION, value) else -> error("Unknown plugin option: ${option.optionName}") } } diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrGenerationExtension.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrGenerationExtension.kt index 4d00d75d..c1eeab54 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrGenerationExtension.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrGenerationExtension.kt @@ -19,21 +19,22 @@ import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName internal class RedactedIrGenerationExtension( private val messageCollector: MessageCollector, private val replacementString: String, - private val redactedAnnotationName: FqName, - private val unRedactedAnnotationName: FqName, + private val redactedAnnotations: Set, + private val unRedactedAnnotations: Set, ) : IrGenerationExtension { override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { val redactedTransformer = RedactedIrVisitor( pluginContext, - redactedAnnotationName, - unRedactedAnnotationName, + redactedAnnotations, + unRedactedAnnotations, replacementString, messageCollector, ) diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrVisitor.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrVisitor.kt index 82feee7e..8f97b441 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrVisitor.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrVisitor.kt @@ -45,6 +45,7 @@ import org.jetbrains.kotlin.ir.util.isPrimitiveArray import org.jetbrains.kotlin.ir.util.parentClassOrNull import org.jetbrains.kotlin.ir.util.primaryConstructor import org.jetbrains.kotlin.ir.util.properties +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.util.OperatorNameConventions @@ -52,8 +53,8 @@ internal const val LOG_PREFIX = "*** REDACTED (IR):" internal class RedactedIrVisitor( private val pluginContext: IrPluginContext, - private val redactedAnnotation: FqName, - private val unRedactedAnnotation: FqName, + private val redactedAnnotations: Set, + private val unRedactedAnnotations: Set, private val replacementString: String, private val messageCollector: MessageCollector, ) : IrElementTransformerVoidWithContext() { @@ -78,10 +79,10 @@ internal class RedactedIrVisitor( primaryConstructor.valueParameters.associateBy { it.name.asString() } val properties = mutableListOf() - val classIsRedacted = declarationParent.hasAnnotation(redactedAnnotation) - val classIsUnredacted = declarationParent.hasAnnotation(unRedactedAnnotation) + val classIsRedacted = redactedAnnotations.any(declarationParent::hasAnnotation) + val classIsUnredacted = unRedactedAnnotations.any(declarationParent::hasAnnotation) val supertypeIsRedacted by unsafeLazy { - declarationParent.getAllSuperclasses().any { it.hasAnnotation(redactedAnnotation) } + declarationParent.getAllSuperclasses().any { redactedAnnotations.any(it::hasAnnotation) } } var anyRedacted = false var anyUnredacted = false @@ -150,10 +151,10 @@ internal class RedactedIrVisitor( } private val IrProperty.isRedacted: Boolean - get() = hasAnnotation(redactedAnnotation) + get() = redactedAnnotations.any(::hasAnnotation) private val IrProperty.isUnredacted: Boolean - get() = hasAnnotation(unRedactedAnnotation) + get() = unRedactedAnnotations.any(::hasAnnotation) /** * The actual body of the toString method. Copied from diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedPlugin.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedPlugin.kt index 5d5e30f0..b9c90375 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedPlugin.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedPlugin.kt @@ -40,25 +40,24 @@ public class RedactedComponentRegistrar : CompilerPluginRegistrar() { val messageCollector = configuration.get(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) val replacementString = checkNotNull(configuration[KEY_REPLACEMENT_STRING]) - val redactedAnnotation = checkNotNull(configuration[KEY_REDACTED_ANNOTATION]) - val unRedactedAnnotation = checkNotNull(configuration[KEY_UNREDACTED_ANNOTATION]) + val redactedAnnotations = checkNotNull(configuration[KEY_REDACTED_ANNOTATIONS]) + .splitToSequence(",") + .mapTo(LinkedHashSet()) { ClassId.fromString(it) } + val unRedactedAnnotations = checkNotNull(configuration[KEY_UNREDACTED_ANNOTATION]) + .splitToSequence(",") + .mapTo(LinkedHashSet()) { ClassId.fromString(it) } val usesK2 = configuration.languageVersionSettings.languageVersion.usesK2 - val redactedAnnotationClassId = ClassId.fromString(redactedAnnotation) - val fqRedactedAnnotation = redactedAnnotationClassId.asSingleFqName() - val unRedactedAnnotationClassId = ClassId.fromString(unRedactedAnnotation) - val fqUnRedactedAnnotation = unRedactedAnnotationClassId.asSingleFqName() if (usesK2) { FirExtensionRegistrarAdapter.registerExtension( - FirRedactedExtensionRegistrar(redactedAnnotationClassId, unRedactedAnnotationClassId) + FirRedactedExtensionRegistrar(redactedAnnotations, unRedactedAnnotations) ) } IrGenerationExtension.registerExtension( RedactedIrGenerationExtension( messageCollector, replacementString, - fqRedactedAnnotation, - fqUnRedactedAnnotation, + redactedAnnotations, unRedactedAnnotations ) ) } diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt index fd714db2..b232d423 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt @@ -15,6 +15,7 @@ */ package dev.zacsweers.redacted.compiler.fir +import dev.zacsweers.redacted.compiler.firstNotNullResult import dev.zacsweers.redacted.compiler.unsafeLazy import org.jetbrains.kotlin.descriptors.isEnumEntry import org.jetbrains.kotlin.descriptors.isObject @@ -31,7 +32,7 @@ import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId -import org.jetbrains.kotlin.fir.declarations.hasAnnotation +import org.jetbrains.kotlin.fir.declarations.toAnnotationClassIdSafe import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.declarations.utils.isExtension @@ -43,6 +44,8 @@ import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.resolvedAnnotationsWithClassIds import org.jetbrains.kotlin.fir.types.ConeErrorType import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.fir.types.coneType @@ -51,11 +54,11 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.util.OperatorNameConventions internal class FirRedactedExtensionRegistrar( - private val redactedAnnotation: ClassId, - private val unRedactedAnnotation: ClassId, + private val redactedAnnotations: Set, + private val unRedactedAnnotations: Set, ) : FirExtensionRegistrar() { override fun ExtensionRegistrarContext.configurePlugin() { - +RedactedFirBuiltIns.getFactory(redactedAnnotation, unRedactedAnnotation) + +RedactedFirBuiltIns.getFactory(redactedAnnotations, unRedactedAnnotations) +::FirRedactedCheckers } } @@ -70,37 +73,37 @@ internal class FirRedactedCheckers(session: FirSession) : FirAdditionalCheckersE internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.Common) { override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { - val classRedactedAnnotation = - declaration.getAnnotationByClassId(context.session.redactedAnnotation, context.session) - val classIsRedacted = classRedactedAnnotation != null - val classUnRedactedAnnotation = - declaration.getAnnotationByClassId(context.session.unRedactedAnnotation, context.session) - val classIsUnRedacted = classUnRedactedAnnotation != null + val classRedactedAnnotations = + context.session.redactedAnnotations.mapNotNull { + declaration.getAnnotationByClassId(it, context.session) + } + val classIsRedacted = classRedactedAnnotations.isNotEmpty() + val classUnRedactedAnnotations = + context.session.unRedactedAnnotations.mapNotNull { + declaration.getAnnotationByClassId(it, context.session) + } + val classIsUnRedacted = classUnRedactedAnnotations.isNotEmpty() val supertypeIsRedacted by unsafeLazy { declaration.superConeTypes.any { if (it is ConeErrorType) return@any false - it.classId - ?.toSymbol(context.session) - ?.hasAnnotation(context.session.redactedAnnotation, context.session) == true + it.classId?.toSymbol(context.session)?.resolvedAnnotationClassIds?.any { + it in context.session.redactedAnnotations + } == true } } - var anyRedacted = false - var anyUnredacted = false + val redactedProperties = mutableMapOf() + val unredactedProperties = mutableMapOf() for (prop in declaration.declarations.filterIsInstance()) { - val isRedacted = prop.isRedacted(context.session) - val isUnredacted = prop.isUnredacted(context.session) - if (isRedacted) { - anyRedacted = true - } - if (isUnredacted) { - anyUnredacted = true - } + prop.redactedAnnotation(context.session)?.let { redactedProperties[prop] = it } + prop.unredactedAnnotation(context.session)?.let { unredactedProperties[prop] = it } } + val anyRedacted = redactedProperties.isNotEmpty() + val anyUnredacted = unredactedProperties.isNotEmpty() - val redactedName = { context.session.redactedAnnotation.shortClassName.asString() } + val redactedName = { redactedProperties.values.first().shortClassName.asString() } - val unRedactedName = { context.session.redactedAnnotation.shortClassName.asString() } + val unRedactedName = { unredactedProperties.values.first().shortClassName.asString() } if (classIsRedacted || supertypeIsRedacted || classIsUnRedacted || anyRedacted) { val customToStringFunction = @@ -149,16 +152,17 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C } if (declaration.classKind.isObject) { if (!supertypeIsRedacted) { + val classAnnotation = classRedactedAnnotations.first() reporter.reportOn( - classRedactedAnnotation!!.source, + classAnnotation.source, RedactedDiagnostics.REDACTED_ERROR, - "@${redactedName()} is useless on object classes.", + "@${classAnnotation.toAnnotationClassIdSafe(context.session)?.shortClassName?.asString()} is useless on object classes.", context, ) return } else if (classIsUnRedacted) { reporter.reportOn( - classUnRedactedAnnotation.source, + classUnRedactedAnnotations.firstOrNull()?.source, RedactedDiagnostics.REDACTED_ERROR, "@${unRedactedName()} is useless on object classes.", context, @@ -200,6 +204,14 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C "@${redactedName()} should only be applied to the class or its properties, not both.", context, ) + for ((redactedProp, annotationId) in redactedProperties) { + reporter.reportOn( + redactedProp.source, + RedactedDiagnostics.REDACTED_ERROR, + "@${annotationId.shortClassName.asString()} should only be applied to the class or its properties, not both.", + context, + ) + } return } // Rest filled in by the IR plugin @@ -213,11 +225,17 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C valueParameters.isEmpty() && returnTypeRef.coneType.fullyExpandedType(session).isString - private fun FirProperty.isRedacted(session: FirSession): Boolean = - hasAnnotation(session.redactedAnnotation, session) + @OptIn(SymbolInternals::class) + private fun FirProperty.redactedAnnotation(session: FirSession): ClassId? = + resolvedAnnotationsWithClassIds(symbol).firstNotNullResult { + it.toAnnotationClassIdSafe(session).takeIf { it in session.redactedAnnotations } + } - private fun FirProperty.isUnredacted(session: FirSession): Boolean = - hasAnnotation(session.unRedactedAnnotation, session) + @OptIn(SymbolInternals::class) + private fun FirProperty.unredactedAnnotation(session: FirSession): ClassId? = + resolvedAnnotationsWithClassIds(symbol).firstNotNullResult { + it.toAnnotationClassIdSafe(session).takeIf { it in session.unRedactedAnnotations } + } private val FirClass.isInstantiableEnum: Boolean get() = isEnumClass && !isExpect && !isExternal diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/RedactedFirBuiltIns.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/RedactedFirBuiltIns.kt index cdcd3756..a1a558be 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/RedactedFirBuiltIns.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/RedactedFirBuiltIns.kt @@ -21,13 +21,13 @@ import org.jetbrains.kotlin.name.ClassId internal class RedactedFirBuiltIns( session: FirSession, - val redactedAnnotation: ClassId, - val unRedactedAnnotation: ClassId, + val redactedAnnotations: Set, + val unRedactedAnnotations: Set, ) : FirExtensionSessionComponent(session) { companion object { - fun getFactory(redactedAnnotation: ClassId, unRedactedAnnotation: ClassId) = + fun getFactory(redactedAnnotations: Set, unRedactedAnnotations: Set) = Factory { session -> - RedactedFirBuiltIns(session, redactedAnnotation, unRedactedAnnotation) + RedactedFirBuiltIns(session, redactedAnnotations, unRedactedAnnotations) } } } @@ -35,8 +35,8 @@ internal class RedactedFirBuiltIns( internal val FirSession.redactedFirBuiltIns: RedactedFirBuiltIns by FirSession.sessionComponentAccessor() -internal val FirSession.redactedAnnotation: ClassId - get() = redactedFirBuiltIns.redactedAnnotation +internal val FirSession.redactedAnnotations: Set + get() = redactedFirBuiltIns.redactedAnnotations -internal val FirSession.unRedactedAnnotation: ClassId - get() = redactedFirBuiltIns.unRedactedAnnotation +internal val FirSession.unRedactedAnnotations: Set + get() = redactedFirBuiltIns.unRedactedAnnotations diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/util.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/util.kt index 18866d68..0b1f9b8b 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/util.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/util.kt @@ -16,3 +16,11 @@ package dev.zacsweers.redacted.compiler internal fun unsafeLazy(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer) + +internal inline fun Iterable.firstNotNullResult(transform: (T) -> R?): R? { + for (element in this) { + val result = transform(element) + if (result != null) return result + } + return null +} \ No newline at end of file diff --git a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt index 16088dbf..05471bb7 100644 --- a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt +++ b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt @@ -24,9 +24,9 @@ import com.tschuchort.compiletesting.PluginOption import com.tschuchort.compiletesting.SourceFile import com.tschuchort.compiletesting.SourceFile.Companion.kotlin import dev.zacsweers.redacted.compiler.RedactedCommandLineProcessor.Companion.OPTION_ENABLED -import dev.zacsweers.redacted.compiler.RedactedCommandLineProcessor.Companion.OPTION_REDACTED_ANNOTATION +import dev.zacsweers.redacted.compiler.RedactedCommandLineProcessor.Companion.OPTION_REDACTED_ANNOTATIONS import dev.zacsweers.redacted.compiler.RedactedCommandLineProcessor.Companion.OPTION_REPLACEMENT_STRING -import dev.zacsweers.redacted.compiler.RedactedCommandLineProcessor.Companion.OPTION_UNREDACTED_ANNOTATION +import dev.zacsweers.redacted.compiler.RedactedCommandLineProcessor.Companion.OPTION_UNREDACTED_ANNOTATIONS import org.jetbrains.kotlin.compiler.plugin.CliOption import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi @@ -497,11 +497,11 @@ class RedactedPluginTest(redactedNames: Pair) { processor.option(OPTION_ENABLED, "true"), processor.option(OPTION_REPLACEMENT_STRING, replacementString ?: "██"), processor.option( - OPTION_REDACTED_ANNOTATION, + OPTION_REDACTED_ANNOTATIONS, redactedAnnotation.pathSegments().joinToString("/"), ), processor.option( - OPTION_UNREDACTED_ANNOTATION, + OPTION_UNREDACTED_ANNOTATIONS, unredactedAnnotation.pathSegments().joinToString("/"), ), ) From 3df993323961ca19f6cf98b25c938b3c6924d60b Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Fri, 20 Dec 2024 21:03:42 -0500 Subject: [PATCH 2/7] Spotless --- README.md | 2 +- .../compiler/RedactedIrGenerationExtension.kt | 1 - .../redacted/compiler/RedactedIrVisitor.kt | 1 - .../redacted/compiler/RedactedPlugin.kt | 21 ++++++++++++------- .../dev/zacsweers/redacted/compiler/util.kt | 2 +- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e206163e..ae379639 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ redacted { // Define custom unredacted annotations. unredactedAnnotations.add("dev/zacsweers/redacted/annotations/Unredacted") // Default - // Define whether this plugin is enabled on this compilation. Useful if you want to + // Define whether this plugin is enabled on this compilation. Useful if you want to // gate this behind a dynamic build configuration. enabled = true // Default diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrGenerationExtension.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrGenerationExtension.kt index c1eeab54..1f8fe6c1 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrGenerationExtension.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrGenerationExtension.kt @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName internal class RedactedIrGenerationExtension( private val messageCollector: MessageCollector, diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrVisitor.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrVisitor.kt index 8f97b441..0f61cd66 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrVisitor.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedIrVisitor.kt @@ -46,7 +46,6 @@ import org.jetbrains.kotlin.ir.util.parentClassOrNull import org.jetbrains.kotlin.ir.util.primaryConstructor import org.jetbrains.kotlin.ir.util.properties import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.util.OperatorNameConventions internal const val LOG_PREFIX = "*** REDACTED (IR):" diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedPlugin.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedPlugin.kt index b9c90375..f8de5276 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedPlugin.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/RedactedPlugin.kt @@ -40,12 +40,18 @@ public class RedactedComponentRegistrar : CompilerPluginRegistrar() { val messageCollector = configuration.get(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) val replacementString = checkNotNull(configuration[KEY_REPLACEMENT_STRING]) - val redactedAnnotations = checkNotNull(configuration[KEY_REDACTED_ANNOTATIONS]) - .splitToSequence(",") - .mapTo(LinkedHashSet()) { ClassId.fromString(it) } - val unRedactedAnnotations = checkNotNull(configuration[KEY_UNREDACTED_ANNOTATION]) - .splitToSequence(",") - .mapTo(LinkedHashSet()) { ClassId.fromString(it) } + val redactedAnnotations = + checkNotNull(configuration[KEY_REDACTED_ANNOTATIONS]).splitToSequence(",").mapTo( + LinkedHashSet() + ) { + ClassId.fromString(it) + } + val unRedactedAnnotations = + checkNotNull(configuration[KEY_UNREDACTED_ANNOTATION]).splitToSequence(",").mapTo( + LinkedHashSet() + ) { + ClassId.fromString(it) + } val usesK2 = configuration.languageVersionSettings.languageVersion.usesK2 if (usesK2) { @@ -57,7 +63,8 @@ public class RedactedComponentRegistrar : CompilerPluginRegistrar() { RedactedIrGenerationExtension( messageCollector, replacementString, - redactedAnnotations, unRedactedAnnotations + redactedAnnotations, + unRedactedAnnotations, ) ) } diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/util.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/util.kt index 0b1f9b8b..b673cadb 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/util.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/util.kt @@ -23,4 +23,4 @@ internal inline fun Iterable.firstNotNullResult(transform: (T) - if (result != null) return result } return null -} \ No newline at end of file +} From 07d0240fa2d30cfc713d17b0cf4635fd0cdac5b8 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Fri, 20 Dec 2024 21:44:49 -0500 Subject: [PATCH 3/7] Reporting fixes --- .../fir/FirRedactedExtensionRegistrar.kt | 84 ++++++++++++++----- .../redacted/compiler/RedactedPluginTest.kt | 11 +-- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt index b232d423..a661e9aa 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt @@ -40,15 +40,17 @@ import org.jetbrains.kotlin.fir.declarations.utils.isExternal import org.jetbrains.kotlin.fir.declarations.utils.isFinal import org.jetbrains.kotlin.fir.declarations.utils.isInline import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName -import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.resolvedAnnotationsWithClassIds +import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.ConeErrorType +import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.fir.types.isString import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.util.OperatorNameConventions @@ -72,6 +74,13 @@ internal class FirRedactedCheckers(session: FirSession) : FirAdditionalCheckersE } internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.Common) { + private class RedactedSupertype( + val ref: FirTypeRef, + val clazz: ConeClassLikeType, + val redactedClassId: ClassId, + ) + + @OptIn(SymbolInternals::class) override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) { val classRedactedAnnotations = context.session.redactedAnnotations.mapNotNull { @@ -83,13 +92,19 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C declaration.getAnnotationByClassId(it, context.session) } val classIsUnRedacted = classUnRedactedAnnotations.isNotEmpty() - val supertypeIsRedacted by unsafeLazy { - declaration.superConeTypes.any { - if (it is ConeErrorType) return@any false - it.classId?.toSymbol(context.session)?.resolvedAnnotationClassIds?.any { - it in context.session.redactedAnnotations - } == true + val redactedSupertype: RedactedSupertype? by unsafeLazy { + for (ref in declaration.superTypeRefs) { + val supertype = ref.coneTypeSafe() ?: continue + if (supertype is ConeErrorType) continue + val redactedAnnotation = + supertype.classId?.toSymbol(context.session)?.resolvedAnnotationClassIds?.firstOrNull { + it in context.session.redactedAnnotations + } + if (redactedAnnotation != null) { + return@unsafeLazy RedactedSupertype(ref, supertype, redactedAnnotation) + } } + null } val redactedProperties = mutableMapOf() @@ -105,7 +120,7 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C val unRedactedName = { unredactedProperties.values.first().shortClassName.asString() } - if (classIsRedacted || supertypeIsRedacted || classIsUnRedacted || anyRedacted) { + if (classIsRedacted || redactedSupertype != null || classIsUnRedacted || anyRedacted) { val customToStringFunction = declaration.declarations.filterIsInstance().find { it.isToStringFromAny(context.session) && it.origin == FirDeclarationOrigin.Source @@ -151,7 +166,7 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C return } if (declaration.classKind.isObject) { - if (!supertypeIsRedacted) { + if (redactedSupertype == null) { val classAnnotation = classRedactedAnnotations.first() reporter.reportOn( classAnnotation.source, @@ -179,7 +194,7 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C ) return } - if (classIsUnRedacted && !supertypeIsRedacted) { + if (classIsUnRedacted && redactedSupertype == null) { reporter.reportOn( declaration.source, RedactedDiagnostics.REDACTED_ERROR, @@ -188,7 +203,7 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C ) return } - if (anyUnredacted && (!classIsRedacted && !supertypeIsRedacted)) { + if (anyUnredacted && (!classIsRedacted && redactedSupertype == null)) { reporter.reportOn( declaration.source, RedactedDiagnostics.REDACTED_ERROR, @@ -197,18 +212,47 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C ) return } - if (!(classIsRedacted xor anyRedacted xor supertypeIsRedacted)) { - reporter.reportOn( - declaration.source, - RedactedDiagnostics.REDACTED_ERROR, - "@${redactedName()} should only be applied to the class or its properties, not both.", - context, - ) - for ((redactedProp, annotationId) in redactedProperties) { + if (!(classIsRedacted xor anyRedacted xor (redactedSupertype != null))) { + val redactedName = + redactedProperties.values.firstOrNull() + ?: classRedactedAnnotations.firstOrNull()?.toAnnotationClassIdSafe(context.session) + ?: redactedSupertype?.redactedClassId + ?: error("Not possible!") + + val message = buildString { + appendLine("@${redactedName.shortClassName.asString()} detected on multiple targets: ") + if (classIsRedacted) { + appendLine("class: '${declaration.nameOrSpecialName.asString()}'") + } + if (anyRedacted) { + appendLine( + "properties: ${redactedProperties.keys.joinToString(", ") { "'${it.name.asString()}'" }}" + ) + } + redactedSupertype?.clazz?.let { appendLine("supertype: ${it.classId}") } + } + + if (classIsRedacted) { + reporter.reportOn( + classRedactedAnnotations.first().source, + RedactedDiagnostics.REDACTED_ERROR, + message, + context, + ) + } else { + // Supertype + reporter.reportOn( + redactedSupertype?.ref?.source, + RedactedDiagnostics.REDACTED_ERROR, + message, + context, + ) + } + for ((redactedProp, _) in redactedProperties) { reporter.reportOn( redactedProp.source, RedactedDiagnostics.REDACTED_ERROR, - "@${annotationId.shortClassName.asString()} should only be applied to the class or its properties, not both.", + message, context, ) } diff --git a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt index 05471bb7..e7e9ce11 100644 --- a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt +++ b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt @@ -239,12 +239,13 @@ class RedactedPluginTest(redactedNames: Pair) { ) assertThat(result.exitCode).isEqualTo(COMPILATION_ERROR) - // Full log is something like this: - // e: /path/to/NonDataClass.kt:5:20 @${redactedAnnotation.shortName().asString() is only - // supported on data classes! - assertThat(result.messages).contains("DoubleAnnotation.kt:") result.assertErrorMessage( - "@${redactedAnnotation.shortName().asString()} should only be applied to the class or its properties" + """ + @${redactedAnnotation.shortName().asString()} detected on multiple targets: + class: 'DoubleAnnotation' + properties: 'a' + """ + .trimIndent() ) } From 09eb0c9af908df5f75d570805593c753aec61f34 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Fri, 20 Dec 2024 21:50:51 -0500 Subject: [PATCH 4/7] Report on the annotation --- .../fir/FirRedactedExtensionRegistrar.kt | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt index a661e9aa..8e4cfddf 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isExternal import org.jetbrains.kotlin.fir.declarations.utils.isFinal import org.jetbrains.kotlin.fir.declarations.utils.isInline import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName +import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.toSymbol @@ -107,8 +108,8 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C null } - val redactedProperties = mutableMapOf() - val unredactedProperties = mutableMapOf() + val redactedProperties = mutableMapOf>() + val unredactedProperties = mutableMapOf>() for (prop in declaration.declarations.filterIsInstance()) { prop.redactedAnnotation(context.session)?.let { redactedProperties[prop] = it } prop.unredactedAnnotation(context.session)?.let { unredactedProperties[prop] = it } @@ -116,9 +117,9 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C val anyRedacted = redactedProperties.isNotEmpty() val anyUnredacted = unredactedProperties.isNotEmpty() - val redactedName = { redactedProperties.values.first().shortClassName.asString() } + val redactedName = { redactedProperties.values.first().second.shortClassName.asString() } - val unRedactedName = { unredactedProperties.values.first().shortClassName.asString() } + val unRedactedName = { unredactedProperties.values.first().second.shortClassName.asString() } if (classIsRedacted || redactedSupertype != null || classIsUnRedacted || anyRedacted) { val customToStringFunction = @@ -214,7 +215,7 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C } if (!(classIsRedacted xor anyRedacted xor (redactedSupertype != null))) { val redactedName = - redactedProperties.values.firstOrNull() + redactedProperties.values.firstOrNull()?.second ?: classRedactedAnnotations.firstOrNull()?.toAnnotationClassIdSafe(context.session) ?: redactedSupertype?.redactedClassId ?: error("Not possible!") @@ -248,9 +249,9 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C context, ) } - for ((redactedProp, _) in redactedProperties) { + for ((_, annotationAndId) in redactedProperties) { reporter.reportOn( - redactedProp.source, + annotationAndId.first.source, RedactedDiagnostics.REDACTED_ERROR, message, context, @@ -270,15 +271,25 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C returnTypeRef.coneType.fullyExpandedType(session).isString @OptIn(SymbolInternals::class) - private fun FirProperty.redactedAnnotation(session: FirSession): ClassId? = + private fun FirProperty.redactedAnnotation(session: FirSession): Pair? = resolvedAnnotationsWithClassIds(symbol).firstNotNullResult { - it.toAnnotationClassIdSafe(session).takeIf { it in session.redactedAnnotations } + val classId = it.toAnnotationClassIdSafe(session) + if (classId != null && classId in session.redactedAnnotations) { + it to classId + } else { + null + } } @OptIn(SymbolInternals::class) - private fun FirProperty.unredactedAnnotation(session: FirSession): ClassId? = + private fun FirProperty.unredactedAnnotation(session: FirSession): Pair? = resolvedAnnotationsWithClassIds(symbol).firstNotNullResult { - it.toAnnotationClassIdSafe(session).takeIf { it in session.unRedactedAnnotations } + val classId = it.toAnnotationClassIdSafe(session) + if (classId != null && classId in session.unRedactedAnnotations) { + it to classId + } else { + null + } } private val FirClass.isInstantiableEnum: Boolean From 28d673dfad664c9ed982d84e297cabe9e9d0eb55 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Fri, 20 Dec 2024 21:54:04 -0500 Subject: [PATCH 5/7] Spotless --- .../dev/zacsweers/redacted/compiler/RedactedPluginTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt index e7e9ce11..80536924 100644 --- a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt +++ b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt @@ -241,7 +241,7 @@ class RedactedPluginTest(redactedNames: Pair) { result.assertErrorMessage( """ - @${redactedAnnotation.shortName().asString()} detected on multiple targets: + @${redactedAnnotation.shortName().asString()} detected on multiple targets: class: 'DoubleAnnotation' properties: 'a' """ From 77d779c0ceefa4a31e8fe4b668100f0d987e597c Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Fri, 20 Dec 2024 21:58:34 -0500 Subject: [PATCH 6/7] Fixes --- .../dev/zacsweers/redacted/compiler/RedactedPluginTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt index 80536924..e7e9ce11 100644 --- a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt +++ b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt @@ -241,7 +241,7 @@ class RedactedPluginTest(redactedNames: Pair) { result.assertErrorMessage( """ - @${redactedAnnotation.shortName().asString()} detected on multiple targets: + @${redactedAnnotation.shortName().asString()} detected on multiple targets: class: 'DoubleAnnotation' properties: 'a' """ From b38fe62060e616164ba15789a5233631ed6bc9bf Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Fri, 20 Dec 2024 21:59:27 -0500 Subject: [PATCH 7/7] Spaces are hard --- .../redacted/compiler/fir/FirRedactedExtensionRegistrar.kt | 2 +- .../dev/zacsweers/redacted/compiler/RedactedPluginTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt index 8e4cfddf..fa162f84 100644 --- a/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt +++ b/redacted-compiler-plugin/src/main/kotlin/dev/zacsweers/redacted/compiler/fir/FirRedactedExtensionRegistrar.kt @@ -221,7 +221,7 @@ internal object FirRedactedDeclarationChecker : FirClassChecker(MppCheckerKind.C ?: error("Not possible!") val message = buildString { - appendLine("@${redactedName.shortClassName.asString()} detected on multiple targets: ") + appendLine("@${redactedName.shortClassName.asString()} detected on multiple targets:") if (classIsRedacted) { appendLine("class: '${declaration.nameOrSpecialName.asString()}'") } diff --git a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt index e7e9ce11..80536924 100644 --- a/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt +++ b/redacted-compiler-plugin/src/test/kotlin/dev/zacsweers/redacted/compiler/RedactedPluginTest.kt @@ -241,7 +241,7 @@ class RedactedPluginTest(redactedNames: Pair) { result.assertErrorMessage( """ - @${redactedAnnotation.shortName().asString()} detected on multiple targets: + @${redactedAnnotation.shortName().asString()} detected on multiple targets: class: 'DoubleAnnotation' properties: 'a' """