From 6f3f21219631ec652209c4270d1a86d101b11505 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Wed, 29 Nov 2023 12:00:16 +0900 Subject: [PATCH] ObjectFinal --- .../src/main/scala/fix/ObjectFinalTest.scala | 48 +++++++ rules/src/main/scala/fix/ObjectFinal.scala | 134 ++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 input/src/main/scala/fix/ObjectFinalTest.scala create mode 100644 rules/src/main/scala/fix/ObjectFinal.scala diff --git a/input/src/main/scala/fix/ObjectFinalTest.scala b/input/src/main/scala/fix/ObjectFinalTest.scala new file mode 100644 index 0000000..72aa489 --- /dev/null +++ b/input/src/main/scala/fix/ObjectFinalTest.scala @@ -0,0 +1,48 @@ +/* +rule = ObjectFinal + */ +package fix + +object ObjectFinalTest { + final val a1: List[String] = List.empty[String] // assert: ObjectFinal + final val a2 = List.empty[String] + + final def b1: List[String] = List.empty[String] // assert: ObjectFinal + final def b2 = List.empty[String] // assert: ObjectFinal + final def b3 = "a" // assert: ObjectFinal + final def b4 = 123 // assert: ObjectFinal + + final val c1: Boolean = false + final val c2: Byte = 2 + final val c3: Short = 3 + final val c4: Char = 'x' + final val c5: Int = 5 + final val c6: Long = 6L + final val c7: Float = 1.5f + final val c8: Double = 2.5 + + final val d1: scala.Boolean = false + final val d2: scala.Byte = 2 + final val d3: scala.Short = 3 + final val d4: scala.Char = 'x' + final val d5: scala.Int = 5 + final val d6: scala.Long = 6L + final val d7: scala.Float = 1.5f + final val d8: scala.Double = 2.5 + + final val e1: _root_.scala.Boolean = false + final val e2: _root_.scala.Byte = 2 + final val e3: _root_.scala.Short = 3 + final val e4: _root_.scala.Char = 'x' + final val e5: _root_.scala.Int = 5 + final val e6: _root_.scala.Long = 6L + final val e7: _root_.scala.Float = 1.5f + final val e8: _root_.scala.Double = 2.5 + + final val s1: String = "s" + final val s2: Predef.String = "s" + final val s3: scala.Predef.String = "s" + final val s4: _root_.scala.Predef.String = "s" + final val s5: java.lang.String = "s" + final val s6: _root_.java.lang.String = "s" +} diff --git a/rules/src/main/scala/fix/ObjectFinal.scala b/rules/src/main/scala/fix/ObjectFinal.scala new file mode 100644 index 0000000..0b8a22f --- /dev/null +++ b/rules/src/main/scala/fix/ObjectFinal.scala @@ -0,0 +1,134 @@ +package fix + +import scala.meta.Defn +import scala.meta.Mod +import scala.meta.Pat +import scala.meta.Term +import scala.meta.Type +import scalafix.Patch +import scalafix.lint.Diagnostic +import scalafix.lint.LintSeverity +import scalafix.v1.SyntacticDocument +import scalafix.v1.SyntacticRule + +object ObjectFinal { + private val constantTypes: Seq[Type] = { + val values = Seq( + "Boolean", + "Byte", + "Short", + "Char", + "Int", + "Long", + "Float", + "Double", + ).map(Type.Name.apply) + + Seq( + values.flatMap { x => + Seq( + x, + Type.Select( + Term.Name("scala"), + x, + ), + Type.Select( + Term.Select( + Term.Name("_root_"), + Term.Name("scala") + ), + x + ) + ) + }, { + val str = Type.Name("String") + + Seq( + str, + Type.Select( + Term.Name("Predef"), + str, + ), + Type.Select( + Term.Select( + Term.Name("scala"), + Term.Name("Predef") + ), + str + ), + Type.Select( + Term.Select( + Term.Select( + Term.Name("_root_"), + Term.Name("scala") + ), + Term.Name("Predef") + ), + str + ), + Type.Select( + Term.Select( + Term.Name("java"), + Term.Name("lang") + ), + str + ), + Type.Select( + Term.Select( + Term.Select( + Term.Name("_root_"), + Term.Name("java") + ), + Term.Name("lang") + ), + str + ), + ) + } + ).flatten + } + + private object FinalMod { + def unapply(values: List[Mod]): Option[Mod] = values.find(_.is[Mod.Final]) + } +} + +class ObjectFinal extends SyntacticRule("ObjectFinal") { + + override def fix(implicit doc: SyntacticDocument): Patch = { + doc.tree.collect { case o: Defn.Object => + o.templ.stats.collect { + case Defn.Val( + ObjectFinal.FinalMod(m), + List(Pat.Var(_: Term.Name)), + Some(tpe), + _ + ) if ObjectFinal.constantTypes.forall(_.structure != tpe.structure) => + Patch.lint( + Diagnostic( + id = "", + message = + "redundant final https://scala-lang.org/files/archive/spec/2.13/06-expressions.html#constant-expressions", + position = m.pos, + severity = LintSeverity.Warning + ) + ) + case Defn.Def.After_4_7_3( + ObjectFinal.FinalMod(m), + _, + _, + _, + _ + ) => + Patch.lint( + Diagnostic( + id = "", + message = "redundant final", + position = m.pos, + severity = LintSeverity.Warning + ) + ) + }.asPatch + }.asPatch + } +}