diff --git a/docs/GUIDE.md b/docs/GUIDE.md
index 8606b8ef..fd1bb46f 100644
--- a/docs/GUIDE.md
+++ b/docs/GUIDE.md
@@ -118,6 +118,13 @@ This option disables detection of messages/events/state based on type of argumen
This option disables detection of messages/events/state based on return type of the function given as argument to method. This detection is enabled by default. If you want to disable it, add the following setting:
`Compile / scalacOptions += "-P:serializability-checker-plugin:--disable-detection-higher-order-function"`
+- `--types-explicitly-marked-as-serializable=,,...`
+
+This option can be used to pass a comma-separated list of fully-qualified names of types that should be considered serializable by the checker, even if they do **not** extend a designated serializability trait.
+The list is empty by default. If you want to mark some types as serializable, add the following setting (here shown with sample types):
+
+`Compile / scalacOptions += "-P:serializability-checker-plugin:--types-explicitly-marked-as-serializable=scala.util.Either,scala.collection.immutable.Set"`
+
### Codec Registration Checker Compiler Plugin
Before using this compiler plugin, make sure that you are using both [annotations](#annotations) properly. If so — the plugin can be used right away. This plugin checks whether classes marked with serializability trait are being referenced in a marked serializer, which ensures that codecs will be registered in runtime.
diff --git a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPlugin.scala b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPlugin.scala
index b84e9cef..39604002 100644
--- a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPlugin.scala
+++ b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPlugin.scala
@@ -27,6 +27,10 @@ class SerializabilityCheckerCompilerPlugin(override val global: Global) extends
pluginOptions.detectFromMethods = !options.contains(disableMethods)
pluginOptions.detectFromUntypedMethods = !options.contains(disableMethodsUntyped)
pluginOptions.detectFromHigherOrderFunctions = !options.contains(disableHigherOrderFunctions)
+ options.find(_.startsWith(typesExplicitlyMarkedAsSerializable)).foreach { opt =>
+ pluginOptions.typesExplicitlyMarkedAsSerializable =
+ opt.stripPrefix(typesExplicitlyMarkedAsSerializable).split(",").toSeq.map(_.strip())
+ }
true
}
@@ -37,6 +41,7 @@ class SerializabilityCheckerCompilerPlugin(override val global: Global) extends
|$disableMethods - disables detection of messages/events/state based on type of arguments to a method, e.g. akka.actor.typed.ActorRef.tell
|$disableMethodsUntyped - disables detection of messages/events/state based on type of arguments to a method that takes Any, used for Akka Classic
|$disableHigherOrderFunctions - disables detection of messages/events/state based on return type of the function given as argument to method
+ |$typesExplicitlyMarkedAsSerializable - comma-separated list of fully-qualified names of types that should be considered serializable by this checker, even if they do NOT extend a designated serializability trait
|""".stripMargin)
}
@@ -49,6 +54,7 @@ object SerializabilityCheckerCompilerPlugin {
val disableMethods = "--disable-detection-methods"
val disableMethodsUntyped = "--disable-detection-untyped-methods"
val disableHigherOrderFunctions = "--disable-detection-higher-order-function"
+ val typesExplicitlyMarkedAsSerializable = "--types-explicitly-marked-as-serializable="
}
val serializabilityTraitType = "org.virtuslab.ash.annotation.SerializabilityTrait"
}
diff --git a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponent.scala b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponent.scala
index 30ee23ad..0b116dd3 100644
--- a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponent.scala
+++ b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponent.scala
@@ -192,6 +192,8 @@ class SerializabilityCheckerCompilerPluginComponent(
Some(tp)
else if (akkaSerializabilityTraits.contains(tp.typeSymbol.fullName))
Some(tp)
+ else if (pluginOptions.typesExplicitlyMarkedAsSerializable.contains(tp.typeSymbol.fullName))
+ Some(tp)
else if (tp.typeSymbol.isAbstractType)
findSuperclassAnnotatedWithSerializabilityTrait(tp.upperBound)
else
diff --git a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerOptions.scala b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerOptions.scala
index b9bef9c5..fe802f30 100644
--- a/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerOptions.scala
+++ b/serializability-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/SerializabilityCheckerOptions.scala
@@ -10,4 +10,5 @@ class SerializabilityCheckerOptions(
var detectFromGenericMethods: Boolean = true,
var detectFromMethods: Boolean = true,
var detectFromUntypedMethods: Boolean = true,
- var detectFromHigherOrderFunctions: Boolean = true)
+ var detectFromHigherOrderFunctions: Boolean = true,
+ var typesExplicitlyMarkedAsSerializable: Seq[String] = Seq.empty)
diff --git a/serializability-checker-compiler-plugin/src/test/resources/TellEitherSeqSetTest.scala b/serializability-checker-compiler-plugin/src/test/resources/TellEitherSeqSetTest.scala
new file mode 100644
index 00000000..8db9b870
--- /dev/null
+++ b/serializability-checker-compiler-plugin/src/test/resources/TellEitherSeqSetTest.scala
@@ -0,0 +1,11 @@
+package org.random.project
+
+import akka.actor.ActorRef
+
+object TellEitherSeqSetTest {
+
+ val ref: ActorRef = ???
+ ref.tell(Right("hello"), null)
+ ref.tell(Seq("hello"), null)
+ ref.tell(Set("hello"), null)
+}
diff --git a/serializability-checker-compiler-plugin/src/test/resources/TellEitherTest.scala b/serializability-checker-compiler-plugin/src/test/resources/TellEitherTest.scala
new file mode 100644
index 00000000..2e075a40
--- /dev/null
+++ b/serializability-checker-compiler-plugin/src/test/resources/TellEitherTest.scala
@@ -0,0 +1,9 @@
+package org.random.project
+
+import akka.actor.ActorRef
+
+object TellEitherTest {
+
+ val ref: ActorRef = ???
+ ref.tell(Right("hello"), null)
+}
diff --git a/serializability-checker-compiler-plugin/src/test/resources/TellSeqTest.scala b/serializability-checker-compiler-plugin/src/test/resources/TellSeqTest.scala
new file mode 100644
index 00000000..06c2ed07
--- /dev/null
+++ b/serializability-checker-compiler-plugin/src/test/resources/TellSeqTest.scala
@@ -0,0 +1,9 @@
+package org.random.project
+
+import akka.actor.ActorRef
+
+object TellSeqTest {
+
+ val ref: ActorRef = ???
+ ref.tell(Seq("hello"), null)
+}
diff --git a/serializability-checker-compiler-plugin/src/test/resources/TellSetTest.scala b/serializability-checker-compiler-plugin/src/test/resources/TellSetTest.scala
new file mode 100644
index 00000000..5c09e4e8
--- /dev/null
+++ b/serializability-checker-compiler-plugin/src/test/resources/TellSetTest.scala
@@ -0,0 +1,9 @@
+package org.random.project
+
+import akka.actor.ActorRef
+
+object TellSetTest {
+
+ val ref: ActorRef = ???
+ ref.tell(Set("hello"), null)
+}
diff --git a/serializability-checker-compiler-plugin/src/test/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponentSpec.scala b/serializability-checker-compiler-plugin/src/test/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponentSpec.scala
index 150e15fe..9353d815 100644
--- a/serializability-checker-compiler-plugin/src/test/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponentSpec.scala
+++ b/serializability-checker-compiler-plugin/src/test/scala/org/virtuslab/ash/SerializabilityCheckerCompilerPluginComponentSpec.scala
@@ -180,5 +180,38 @@ class SerializabilityCheckerCompilerPluginComponentSpec extends AnyWordSpecLike
val code = getResourceAsString("GenericsTest3.scala")
SerializabilityCheckerCompiler.compileCode(List(serNoCode, code)) should be("")
}
+
+ "fail on usage of Either as a message" in {
+ val code = getResourceAsString("TellEitherTest.scala")
+ SerializabilityCheckerCompiler.compileCode(List(code)) should include(
+ "Right[Nothing,String] is used as Akka message")
+ }
+
+ "fail on usage of Seq as a message" in {
+ val code = getResourceAsString("TellSeqTest.scala")
+ SerializabilityCheckerCompiler.compileCode(List(code)) should include("Seq[String] is used as Akka message")
+ }
+
+ "fail on usage of Set as a message" in {
+ val code = getResourceAsString("TellSetTest.scala")
+ SerializabilityCheckerCompiler.compileCode(List(code)) should include("Set[String] is used as Akka message")
+ }
+
+ "not fail on usage of Either as a message when Either is explicitly marked as serializable" in {
+ val code = getResourceAsString("TellEitherTest.scala")
+ SerializabilityCheckerCompiler.compileCode(
+ List(code),
+ List(typesExplicitlyMarkedAsSerializable + "scala.util.Either")) should be("")
+ }
+
+ "when multiple types are used as a message, then only fail on the ones that are NOT explicitly marked as serializable" in {
+ val code = getResourceAsString("TellEitherSeqSetTest.scala")
+ val output = SerializabilityCheckerCompiler.compileCode(
+ List(code),
+ List(typesExplicitlyMarkedAsSerializable + "scala.util.Either,scala.collection.immutable.Set"))
+ output should include("Seq[String] is used as Akka message")
+ (output should not).include("Right[Nothing,String] is used as Akka message")
+ (output should not).include("Set[String] is used as Akka message")
+ }
}
}