Enforce ACC_VOLATILE/ACC_FINAL exclusivity
diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt
index 5aba2b8..557d4a6 100644
--- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt
+++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/ResolverAAImpl.kt
@@ -263,8 +263,11 @@
         Origin.SYNTHETIC -> existingJavaModifiers(declaration)
     }
 
-    private fun existingJavaModifiers(declaration: KSDeclaration): Set<Modifier> =
-        declaration.modifiers.filter { it in javaModifiers }.toSet()
+    private fun existingJavaModifiers(declaration: KSDeclaration): Set<Modifier> {
+        val modifiers = declaration.modifiers.filter { it in javaModifiers }.toSet()
+        // According to the JVM specification, ACC_VOLATILE and ACC_FINAL are mutually exclusive, so we defer to ACC_VOLATILE
+        return if (isVolatile(declaration)) modifiers - Modifier.FINAL else modifiers
+    }
 
     private fun visibilityModifier(declaration: KSDeclaration): Modifier? = when (declaration.getVisibility()) {
         // TODO: getVisibility might be inlined here or simplified here.
@@ -303,8 +306,17 @@
         else -> null
     }
 
+    private fun isVolatile(declaration: KSDeclaration): Boolean = when (declaration.origin) {
+        Origin.KOTLIN -> declaration.annotations.any { it.resolvesTo(JVM_VOLATILE_ANNOTATION_FQN) }
+        Origin.KOTLIN_LIB, Origin.JAVA_LIB -> volatileModifierIfApplicableTo(declaration) != null
+        Origin.JAVA -> declaration.modifiers.contains(Modifier.JAVA_VOLATILE)
+        Origin.SYNTHETIC -> false
+    }
+
     private fun finalModifierIfApplicableTo(declaration: KSDeclaration): Modifier? = when (declaration.origin) {
-        Origin.KOTLIN if declaration !is KSBackingField && !declaration.isOpen() -> Modifier.FINAL
+        // According to the JVM specification, ACC_VOLATILE and ACC_FINAL are mutually exclusive, so we defer to ACC_VOLATILE
+        Origin.KOTLIN if declaration !is KSBackingField && !declaration.isOpen() && !isVolatile(declaration) ->
+            Modifier.FINAL
         else -> null
     }
 
diff --git a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/processor/JavaModifierProcessor.kt b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/processor/JavaModifierProcessor.kt
index 118fb08..901faa1 100644
--- a/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/processor/JavaModifierProcessor.kt
+++ b/kotlin-analysis-api/src/test/kotlin/com/google/devtools/ksp/processor/JavaModifierProcessor.kt
@@ -90,7 +90,7 @@
                 ?: "${parentDeclaration?.qualifiedName?.asString()}.${simpleName.asString()}"
             val modifiersSignature = modifiers.map { it.toString() }.sorted().joinToString(" ")
             val extras = resolver.effectiveJavaModifiers(this).map { it.toString() }.sorted().joinToString(" ").trim()
-            return "$id: $modifiersSignature".trim() + " : " + extras
+            return ("$id: $modifiersSignature".trim() + " : " + extras).trim()
         }
     }
 }
diff --git a/kotlin-analysis-api/testData/javaModifiers.kt b/kotlin-analysis-api/testData/javaModifiers.kt
index 85fdf6c..1531b44 100644
--- a/kotlin-analysis-api/testData/javaModifiers.kt
+++ b/kotlin-analysis-api/testData/javaModifiers.kt
@@ -51,7 +51,7 @@
 // EXPECT NEXT: DependencyOuterJavaClass.transientField.field: JAVA_TRANSIENT : JAVA_TRANSIENT
 // DependencyOuterJavaClass.transientField: FINAL : FINAL JAVA_TRANSIENT
 // EXPECT NEXT: DependencyOuterJavaClass.volatileField.field: JAVA_VOLATILE : JAVA_VOLATILE
-// DependencyOuterJavaClass.volatileField: FINAL : FINAL JAVA_VOLATILE
+// DependencyOuterJavaClass.volatileField: FINAL : JAVA_VOLATILE
 // DependencyOuterJavaClass: OPEN PUBLIC : PUBLIC
 // DependencyOuterKotlinClass.<init>: FINAL PUBLIC : FINAL PUBLIC
 // DependencyOuterKotlinClass.Companion.<init>: FINAL PRIVATE : FINAL PRIVATE
@@ -78,7 +78,7 @@
 // EXPECT NEXT: DependencyOuterKotlinClass.transientProperty.field: FINAL PRIVATE : FINAL PRIVATE
 // DependencyOuterKotlinClass.transientProperty: FINAL PUBLIC : FINAL JAVA_TRANSIENT PUBLIC
 // EXPECT NEXT: DependencyOuterKotlinClass.volatileProperty.field: PRIVATE : PRIVATE
-// DependencyOuterKotlinClass.volatileProperty: FINAL PUBLIC : FINAL JAVA_VOLATILE PUBLIC
+// DependencyOuterKotlinClass.volatileProperty: FINAL PUBLIC : JAVA_VOLATILE PUBLIC
 // DependencyOuterKotlinClass: OPEN PUBLIC : PUBLIC
 // HasTypeAliasFuns: Modifiers: []
 // HasTypeAliasFuns: Visibility: PUBLIC
@@ -102,6 +102,11 @@
 // EXPECT NEXT: OuterJavaClass.staticPublicField.field: JAVA_STATIC PUBLIC : JAVA_STATIC PUBLIC
 // OuterJavaClass.staticPublicField: JAVA_STATIC PUBLIC : JAVA_STATIC PUBLIC
 // OuterJavaClass.staticPublicMethod: JAVA_STATIC PUBLIC : JAVA_STATIC PUBLIC
+// OuterJavaClass.synchronizedFun: JAVA_SYNCHRONIZED : JAVA_SYNCHRONIZED
+// EXPECT NEXT: OuterJavaClass.transientField.field: JAVA_TRANSIENT : JAVA_TRANSIENT
+// OuterJavaClass.transientField: JAVA_TRANSIENT : JAVA_TRANSIENT
+// EXPECT NEXT: OuterJavaClass.volatileField.field: JAVA_VOLATILE : JAVA_VOLATILE
+// OuterJavaClass.volatileField: JAVA_VOLATILE : JAVA_VOLATILE
 // OuterJavaClass: PUBLIC : PUBLIC
 // OuterKotlinClass.<init>: FINAL PUBLIC : FINAL PUBLIC
 // OuterKotlinClass.Companion.<init>: FINAL PUBLIC : FINAL PUBLIC
@@ -126,7 +131,7 @@
 // OuterKotlinClass.mutableProperty: : FINAL PUBLIC
 // OuterKotlinClass.synchronizedFun: : FINAL JAVA_SYNCHRONIZED PUBLIC
 // EXPECT CURRENT: OuterKotlinClass.transientProperty: : FINAL JAVA_TRANSIENT PUBLIC
-// EXPECT CURRENT: OuterKotlinClass.volatileProperty: : FINAL JAVA_VOLATILE PUBLIC
+// EXPECT CURRENT: OuterKotlinClass.volatileProperty: : JAVA_VOLATILE PUBLIC
 // EXPECT NEXT: OuterKotlinClass.transientProperty.field: FINAL PRIVATE : FINAL JAVA_TRANSIENT PRIVATE
 // EXPECT NEXT: OuterKotlinClass.transientProperty: : FINAL PUBLIC
 // EXPECT NEXT: OuterKotlinClass.volatileProperty.field: PRIVATE : JAVA_VOLATILE PRIVATE
@@ -262,6 +267,9 @@
     protected static String staticProtectedField;
     private static void staticPrivateMethod() {}
     private static String staticPrivateField;
+    transient String transientField = "";
+    volatile String volatileField = "";
+    synchronized String synchronizedFun() { return ""; }
 }
 // FILE: OuterKotlinClass.kt
 typealias CustomJvmStatic=JvmStatic