All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fix.ExtendsProductWithSerializable.scala Maven / Gradle / Ivy

The newest version!
package fix

import scala.meta.Ctor
import scala.meta.Defn
import scala.meta.Mod
import scala.meta.Template
import scala.meta.Type
import scala.meta.XtensionClassifiable
import scala.meta.XtensionCollectionLikeUI
import scala.meta.typeParamClauseToValues
import scalafix.Patch
import scalafix.v1.SyntacticDocument
import scalafix.v1.SyntacticRule
import scalafix.v1.XtensionSeqPatch

/**
 * @see [[https://nrinaudo.github.io/scala-best-practices/adts/product_with_serializable.html]]
 * @see [[https://typelevel.org/blog/2018/05/09/product-with-serializable.html]]
 * @see [[https://github.com/scala/bug/issues/9173]]
 */
class ExtendsProductWithSerializable extends SyntacticRule("ExtendsProductWithSerializable") {
  private[this] def emptyConstructor(ctor: Ctor.Primary): Boolean = {
    ctor.mods.isEmpty && ctor.name.value.isEmpty && ctor.paramClauses.isEmpty
  }

  override def fix(implicit doc: SyntacticDocument): Patch = {
    val sealedClasses = doc.tree.collect {
      case t: Defn.Class
          if t.mods.exists(_.is[Mod.Sealed]) && t.mods.exists(
            _.is[Mod.Abstract]
          ) && !t.mods.exists(_.is[Mod.Case]) && t.templ.inits.isEmpty && t.tparamClause.isEmpty =>
        if (emptyConstructor(t.ctor)) {
          t.name -> t.name
        } else {
          t.name -> t.ctor
        }
      case t: Defn.Trait if t.mods.exists(_.is[Mod.Sealed]) && t.templ.inits.isEmpty && t.tparamClause.isEmpty =>
        if (emptyConstructor(t.ctor)) {
          t.name -> t.name
        } else {
          t.name -> t.ctor
        }
    }
    sealedClasses.map { case (sealedClass, ctorOrName) =>
      def f(templ: Template): Boolean =
        templ.inits.iterator.map(_.tpe).collect { case Type.Name(y) if sealedClass.value == y => () }.nonEmpty

      if (
        doc.tree.collect {
          case t: Defn.Trait if f(t.templ) =>
            ()
        }.isEmpty
      ) {
        val subClasses = doc.tree.collect {
          case t: Defn.Class if f(t.templ) =>
            t.mods
          case t: Defn.Object if f(t.templ) =>
            t.mods
        }
        if (subClasses.nonEmpty && subClasses.forall(_.exists(_.is[Mod.Case]))) {
          Patch.addRight(ctorOrName, " extends Product with Serializable")
        } else {
          Patch.empty
        }
      } else {
        Patch.empty
      }
    }.asPatch
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy