From d7df049ddfe88099871757a65c1d41c62d9677e1 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Fri, 3 Jan 2025 15:40:24 +0900 Subject: [PATCH 01/10] sbt 1.10.7 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 09feeee..73df629 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.4 +sbt.version=1.10.7 From a3b34daca43ab0f08df83706e99472aea4093065 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Tue, 5 Nov 2024 16:50:03 +0900 Subject: [PATCH 02/10] add SameParamOverloadingTest2 --- .../scala-3/fix/SameParamOverloadingTest2.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 input/src/main/scala-3/fix/SameParamOverloadingTest2.scala diff --git a/input/src/main/scala-3/fix/SameParamOverloadingTest2.scala b/input/src/main/scala-3/fix/SameParamOverloadingTest2.scala new file mode 100644 index 0000000..11f4d62 --- /dev/null +++ b/input/src/main/scala-3/fix/SameParamOverloadingTest2.scala @@ -0,0 +1,14 @@ +/* +rule = SameParamOverloading + */ +package fix + +import scala.reflect.ClassTag + +object SameParamOverloadingTest2 { + + abstract class A1 { + def x1[A](a: A)(using ClassTag[A]): Int // assert: SameParamOverloading + def x1[A](b: A): String // assert: SameParamOverloading + } +} From ef067df6ee8424fe3907192cbbd5a0a46b84ef84 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Sat, 7 Dec 2024 09:55:32 +0900 Subject: [PATCH 03/10] fix CollectHeadOption (cherry picked from commit 3dd9cfe0f2c32d2ccf41aeb1955285ee62f803e9) --- input/src/main/scala/fix/CollectHeadOptionTest.scala | 1 + output/src/main/scala/fix/CollectHeadOptionTest.scala | 1 + rules/src/main/scala/fix/CollectHeadOption.scala | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/input/src/main/scala/fix/CollectHeadOptionTest.scala b/input/src/main/scala/fix/CollectHeadOptionTest.scala index fc3ba16..1ac2f91 100644 --- a/input/src/main/scala/fix/CollectHeadOptionTest.scala +++ b/input/src/main/scala/fix/CollectHeadOptionTest.scala @@ -5,4 +5,5 @@ package fix object CollectHeadOptionTest { def x1: Option[String] = List(1, 2, 3).collect { case n if n % 2 == 0 => n.toString }.headOption + def x2(f: PartialFunction[Int, String]): Option[String] = List(1, 2, 3).collect(f).headOption } diff --git a/output/src/main/scala/fix/CollectHeadOptionTest.scala b/output/src/main/scala/fix/CollectHeadOptionTest.scala index 52b6f62..e816244 100644 --- a/output/src/main/scala/fix/CollectHeadOptionTest.scala +++ b/output/src/main/scala/fix/CollectHeadOptionTest.scala @@ -2,4 +2,5 @@ package fix object CollectHeadOptionTest { def x1: Option[String] = List(1, 2, 3).collectFirst{ case n if n % 2 == 0 => n.toString } + def x2(f: PartialFunction[Int, String]): Option[String] = List(1, 2, 3).collectFirst(f) } diff --git a/rules/src/main/scala/fix/CollectHeadOption.scala b/rules/src/main/scala/fix/CollectHeadOption.scala index 21e08a6..40d5613 100644 --- a/rules/src/main/scala/fix/CollectHeadOption.scala +++ b/rules/src/main/scala/fix/CollectHeadOption.scala @@ -17,7 +17,12 @@ class CollectHeadOption extends SyntacticRule("CollectHeadOption") { ), Term.Name("headOption") ) => - Patch.replaceTree(t, s"${obj}.collectFirst${func}") + func match { + case _: Term.PartialFunction => + Patch.replaceTree(t, s"${obj}.collectFirst${func}") + case _ => + Patch.replaceTree(t, s"${obj}.collectFirst(${func})") + } } }.asPatch } From 6506b1e8c5edd33d9c9c5cad0c2418aca42f612e Mon Sep 17 00:00:00 2001 From: Scala Steward <2517319+scala-steward-bot@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:53:27 +0000 Subject: [PATCH 04/10] Update sbt-pgp to 2.3.1 (cherry picked from commit b8810a2a0649b92730ab00131bab34483bd4f5cf) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 18257b7..e99ccdf 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,7 +4,7 @@ addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.0") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.12.2") -addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.0") +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") From 856a44fad9a6a88897ea55a2c99a8979ca5c3353 Mon Sep 17 00:00:00 2001 From: Scala Steward <2517319+scala-steward-bot@users.noreply.github.com> Date: Sun, 24 Nov 2024 08:46:54 +0000 Subject: [PATCH 05/10] Update sbt-projectmatrix to 0.10.1 (cherry picked from commit 38d285370476ea3add9a2ed504b3b49f62880eba) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index e99ccdf..dd7fc61 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,6 +1,6 @@ addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.1") -addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.0") +addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.1") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.12.2") From dea9f8c345d4141143f67fd9b0cb6728d0c0187e Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Fri, 3 Jan 2025 12:48:50 +0900 Subject: [PATCH 06/10] WithLeftWithRight (cherry picked from commit a49b86f15f3498d235fc995bdb08b9a6d23e00d7) --- .../scala-3/fix/WithLeftWithRightTest.scala | 9 ++++++ .../scala-3/fix/WithLeftWithRightTest.scala | 6 ++++ .../main/scala/fix/WithLeftWithRight.scala | 32 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 input/src/main/scala-3/fix/WithLeftWithRightTest.scala create mode 100644 output/src/main/scala-3/fix/WithLeftWithRightTest.scala create mode 100644 rules/src/main/scala/fix/WithLeftWithRight.scala diff --git a/input/src/main/scala-3/fix/WithLeftWithRightTest.scala b/input/src/main/scala-3/fix/WithLeftWithRightTest.scala new file mode 100644 index 0000000..89fe3dc --- /dev/null +++ b/input/src/main/scala-3/fix/WithLeftWithRightTest.scala @@ -0,0 +1,9 @@ +/* +rule = WithLeftWithRight + */ +package fix + +trait WithLeftWithRightTest { + def x1: Either[String, Int] = Right[String, Int](2) + def x2: Either[String, Int] = Left[String, Int]("a") +} diff --git a/output/src/main/scala-3/fix/WithLeftWithRightTest.scala b/output/src/main/scala-3/fix/WithLeftWithRightTest.scala new file mode 100644 index 0000000..1f1bb8e --- /dev/null +++ b/output/src/main/scala-3/fix/WithLeftWithRightTest.scala @@ -0,0 +1,6 @@ +package fix + +trait WithLeftWithRightTest { + def x1: Either[String, Int] = Right(2).withLeft[String] + def x2: Either[String, Int] = Left("a").withRight[Int] +} diff --git a/rules/src/main/scala/fix/WithLeftWithRight.scala b/rules/src/main/scala/fix/WithLeftWithRight.scala new file mode 100644 index 0000000..136b39a --- /dev/null +++ b/rules/src/main/scala/fix/WithLeftWithRight.scala @@ -0,0 +1,32 @@ +package fix + +import scala.meta.Term +import scala.meta.Type +import scala.meta.XtensionCollectionLikeUI +import scalafix.Patch +import scalafix.v1.SyntacticDocument +import scalafix.v1.SyntacticRule +import scalafix.v1.XtensionSeqPatch + +class WithLeftWithRight extends SyntacticRule("WithLeftWithRight") { + override def fix(implicit doc: SyntacticDocument): Patch = { + doc.tree.collect { + case t @ Term.Apply.After_4_6_0( + Term.ApplyType.After_4_6_0( + Term.Name("Right"), + Type.ArgClause(left :: _ :: Nil) + ), + Term.ArgClause(value :: Nil, None) + ) => + Patch.replaceTree(t, s"Right(${value}).withLeft[$left]") + case t @ Term.Apply.After_4_6_0( + Term.ApplyType.After_4_6_0( + Term.Name("Left"), + Type.ArgClause(_ :: right :: Nil) + ), + Term.ArgClause(value :: Nil, None) + ) => + Patch.replaceTree(t, s"Left(${value}).withRight[$right]") + }.asPatch + } +} From e39ad59e618e9f21254a3b830b86790d2baad00b Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Fri, 3 Jan 2025 13:08:05 +0900 Subject: [PATCH 07/10] UsingParamAnonymous (cherry picked from commit 0c0e8616b075bec7aba797b44fcdb2bed9f8d6b8) --- .../scala-3/fix/UsingParamAnonymousTest.scala | 9 ++++ .../scala-3/fix/UsingParamAnonymousTest.scala | 6 +++ .../main/scala/fix/UsingParamAnonymous.scala | 42 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 input/src/main/scala-3/fix/UsingParamAnonymousTest.scala create mode 100644 output/src/main/scala-3/fix/UsingParamAnonymousTest.scala create mode 100644 rules/src/main/scala/fix/UsingParamAnonymous.scala diff --git a/input/src/main/scala-3/fix/UsingParamAnonymousTest.scala b/input/src/main/scala-3/fix/UsingParamAnonymousTest.scala new file mode 100644 index 0000000..471dcff --- /dev/null +++ b/input/src/main/scala-3/fix/UsingParamAnonymousTest.scala @@ -0,0 +1,9 @@ +/* +rule = UsingParamAnonymous + */ +package fix + +trait UsingParamAnonymousTest { + def f1(using a1: Int, a2: Int): Int = List(a1, 2).sum + def f2(using a1: Int, a2: String): Int = summon[Int] + summon[String].size +} diff --git a/output/src/main/scala-3/fix/UsingParamAnonymousTest.scala b/output/src/main/scala-3/fix/UsingParamAnonymousTest.scala new file mode 100644 index 0000000..0030307 --- /dev/null +++ b/output/src/main/scala-3/fix/UsingParamAnonymousTest.scala @@ -0,0 +1,6 @@ +package fix + +trait UsingParamAnonymousTest { + def f1(using a1: Int, a2: Int): Int = List(a1, 2).sum + def f2(using Int, String): Int = summon[Int] + summon[String].size +} diff --git a/rules/src/main/scala/fix/UsingParamAnonymous.scala b/rules/src/main/scala/fix/UsingParamAnonymous.scala new file mode 100644 index 0000000..46640eb --- /dev/null +++ b/rules/src/main/scala/fix/UsingParamAnonymous.scala @@ -0,0 +1,42 @@ +package fix + +import scala.meta.Defn +import scala.meta.Mod +import scala.meta.Term +import scala.meta.XtensionClassifiable +import scala.meta.XtensionCollectionLikeUI +import scala.meta.tokens.Token +import scalafix.Patch +import scalafix.v1.SyntacticDocument +import scalafix.v1.SyntacticRule +import scalafix.v1.XtensionSeqPatch + +class UsingParamAnonymous extends SyntacticRule("UsingParamAnonymous") { + override def fix(implicit doc: SyntacticDocument): Patch = { + doc.tree.collect { case t: Defn.Def => + t.paramClauses.filter { paramClause => + paramClause.values.forall( + _.mods.exists(_.is[Mod.Using]) + ) && paramClause.values.forall( + !_.mods.exists(_.is[Mod.Inline]) + ) + }.map { paramClause => + val countOneNames = t.collect { case x: Term.Name => + x.value + }.groupBy(identity).filter(_._2.size == 1).keySet + if (paramClause.values.forall(a => countOneNames(a.name.value))) { + paramClause.values + .map(p => + Seq( + Patch.removeTokens(p.name.tokens), + Patch.removeToken(p.tokens.find(_.is[Token.Colon]).get) + ).asPatch + ) + .asPatch + } else { + Patch.empty + } + }.asPatch + }.asPatch + } +} From 719e82a32ddf1b2cb7528a7f474d44896b1de2d4 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Fri, 3 Jan 2025 14:37:32 +0900 Subject: [PATCH 08/10] MatchParentheses (cherry picked from commit d10c78220cdddc5d21a36da1a2fd2bfae7adb206) --- .../scala-3/fix/MatchParenthesesTest.scala | 18 ++++++++++ .../scala-3/fix/MatchParenthesesTest.scala | 15 ++++++++ .../src/main/scala/fix/MatchParentheses.scala | 34 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 input/src/main/scala-3/fix/MatchParenthesesTest.scala create mode 100644 output/src/main/scala-3/fix/MatchParenthesesTest.scala create mode 100644 rules/src/main/scala/fix/MatchParentheses.scala diff --git a/input/src/main/scala-3/fix/MatchParenthesesTest.scala b/input/src/main/scala-3/fix/MatchParenthesesTest.scala new file mode 100644 index 0000000..0452b77 --- /dev/null +++ b/input/src/main/scala-3/fix/MatchParenthesesTest.scala @@ -0,0 +1,18 @@ +/* +rule = MatchParentheses + */ +package fix + +trait MatchParenthesesTest { + def f1(x: List[Int]): Int = (x match { + case Nil => "," + case _ => x.mkString(",") + }).length + + def g(s: String): String + + def f2(x: List[Int]): Int = g(x match { + case Nil => "," + case _ => x.mkString(",") + }).length +} diff --git a/output/src/main/scala-3/fix/MatchParenthesesTest.scala b/output/src/main/scala-3/fix/MatchParenthesesTest.scala new file mode 100644 index 0000000..768c3a6 --- /dev/null +++ b/output/src/main/scala-3/fix/MatchParenthesesTest.scala @@ -0,0 +1,15 @@ +package fix + +trait MatchParenthesesTest { + def f1(x: List[Int]): Int = x .match { + case Nil => "," + case _ => x.mkString(",") + }.length + + def g(s: String): String + + def f2(x: List[Int]): Int = g(x match { + case Nil => "," + case _ => x.mkString(",") + }).length +} diff --git a/rules/src/main/scala/fix/MatchParentheses.scala b/rules/src/main/scala/fix/MatchParentheses.scala new file mode 100644 index 0000000..46aca9e --- /dev/null +++ b/rules/src/main/scala/fix/MatchParentheses.scala @@ -0,0 +1,34 @@ +package fix + +import scala.meta.Term +import scala.meta.XtensionClassifiable +import scala.meta.XtensionCollectionLikeUI +import scala.meta.tokens.Token +import scalafix.Patch +import scalafix.v1.SyntacticDocument +import scalafix.v1.SyntacticRule +import scalafix.v1.XtensionOptionPatch +import scalafix.v1.XtensionSeqPatch + +class MatchParentheses extends SyntacticRule("MatchParentheses") { + override def fix(implicit doc: SyntacticDocument): Patch = { + doc.tree.collect { case t: Term.Match => + PartialFunction + .condOpt(doc.tokens.filter(_.pos.start >= t.pos.end).take(2)) { case Seq(r: Token.RightParen, _: Token.Dot) => + PartialFunction + .condOpt(doc.tokens.filter(_.pos.end <= t.pos.start).takeRight(2)) { + case Seq(n, l: Token.LeftParen) if !n.is[Token.Ident] => + Seq( + Patch.removeToken(l), + t.tokens.collectFirst { case m: Token.KwMatch => + Patch.addLeft(m, ".") + }.asPatch, + Patch.removeToken(r) + ).asPatch + } + .asPatch + } + .asPatch + }.asPatch + } +} From a7af0dca0393fdbdc29ff2ecf679b77608fae346 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Fri, 3 Jan 2025 14:53:47 +0900 Subject: [PATCH 09/10] ForTupleMatch (cherry picked from commit 8358702d1fe21bce080d1b5669200c77754ee89c) --- .../main/scala-3/fix/ForTupleMatchTest.scala | 38 +++++++++++++++++++ .../main/scala-3/fix/ForTupleMatchTest.scala | 35 +++++++++++++++++ rules/src/main/scala/fix/ForTupleMatch.scala | 38 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 input/src/main/scala-3/fix/ForTupleMatchTest.scala create mode 100644 output/src/main/scala-3/fix/ForTupleMatchTest.scala create mode 100644 rules/src/main/scala/fix/ForTupleMatch.scala diff --git a/input/src/main/scala-3/fix/ForTupleMatchTest.scala b/input/src/main/scala-3/fix/ForTupleMatchTest.scala new file mode 100644 index 0000000..22a10b9 --- /dev/null +++ b/input/src/main/scala-3/fix/ForTupleMatchTest.scala @@ -0,0 +1,38 @@ +/* +rule = ForTupleMatch + */ +package fix + +trait ForTupleMatchTest { + def x1: List[(Int, String)] + def x2: List[(Int, String, Boolean)] + + def f0: List[(String, Int)] = for { + a <- x2 + (b, c, _) = a + } yield (c, b) + + def f1: List[(String, Int)] = for { + a <- x1 + (b, c) = a + } yield (c, b) + + def f2: List[((Int, String), Int)] = for { + a <- x1 + (b, c) = a + } yield (a, b) + + def f3: Unit = for { + a <- x1 + (b, c) = a + } { + println(b) + } + + def f4: Unit = for { + a <- x1 + (b, c) = a + } { + println(a._2) + } +} diff --git a/output/src/main/scala-3/fix/ForTupleMatchTest.scala b/output/src/main/scala-3/fix/ForTupleMatchTest.scala new file mode 100644 index 0000000..3def366 --- /dev/null +++ b/output/src/main/scala-3/fix/ForTupleMatchTest.scala @@ -0,0 +1,35 @@ +package fix + +trait ForTupleMatchTest { + def x1: List[(Int, String)] + def x2: List[(Int, String, Boolean)] + + def f0: List[(String, Int)] = for { + (b, c, _) <- x2 + + } yield (c, b) + + def f1: List[(String, Int)] = for { + (b, c) <- x1 + + } yield (c, b) + + def f2: List[((Int, String), Int)] = for { + a <- x1 + (b, c) = a + } yield (a, b) + + def f3: Unit = for { + (b, c) <- x1 + + } { + println(b) + } + + def f4: Unit = for { + a <- x1 + (b, c) = a + } { + println(a._2) + } +} diff --git a/rules/src/main/scala/fix/ForTupleMatch.scala b/rules/src/main/scala/fix/ForTupleMatch.scala new file mode 100644 index 0000000..2277fc6 --- /dev/null +++ b/rules/src/main/scala/fix/ForTupleMatch.scala @@ -0,0 +1,38 @@ +package fix + +import scala.meta.Enumerator +import scala.meta.Pat +import scala.meta.Term +import scala.meta.XtensionCollectionLikeUI +import scalafix.Patch +import scalafix.v1.SyntacticDocument +import scalafix.v1.SyntacticRule +import scalafix.v1.XtensionSeqPatch + +class ForTupleMatch extends SyntacticRule("ForTupleMatch") { + override def fix(implicit doc: SyntacticDocument): Patch = { + doc.tree.collect { + case t: Term.For => + t + case t: Term.ForYield => + t + }.map { t => + t.enums + .zip(t.enums.drop(1)) + .collect { + case ( + Enumerator.Generator(Pat.Var(tuple1: Term.Name), _), + tupleVal @ Enumerator.Val( + extracted: Pat.Tuple, + Term.Name(tuple2) + ) + ) if tuple1.value == tuple2 && (t.collect { case Term.Name(x) if x == tuple1.value => () }.size == 2) => + Seq( + Patch.replaceTree(tuple1, extracted.toString), + Patch.removeTokens(tupleVal.tokens) + ).asPatch + } + .asPatch + }.asPatch + } +} From bcf990cf7bb4c63fd518d989a2a664bc18285bef Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Fri, 3 Jan 2025 15:19:38 +0900 Subject: [PATCH 10/10] UsingParamAnonymousConstructor (cherry picked from commit 04ab3aceaa4c3aa8c5178c40cb34f075a5935067) --- .../UsingParamAnonymousConstructorTest.scala | 18 ++++++++ .../UsingParamAnonymousConstructorTest.scala | 15 +++++++ .../fix/UsingParamAnonymousConstructor.scala | 41 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 input/src/main/scala-3/fix/UsingParamAnonymousConstructorTest.scala create mode 100644 output/src/main/scala-3/fix/UsingParamAnonymousConstructorTest.scala create mode 100644 rules/src/main/scala/fix/UsingParamAnonymousConstructor.scala diff --git a/input/src/main/scala-3/fix/UsingParamAnonymousConstructorTest.scala b/input/src/main/scala-3/fix/UsingParamAnonymousConstructorTest.scala new file mode 100644 index 0000000..46ee0b0 --- /dev/null +++ b/input/src/main/scala-3/fix/UsingParamAnonymousConstructorTest.scala @@ -0,0 +1,18 @@ +/* +rule = UsingParamAnonymousConstructor + */ +package fix + +trait UsingParamAnonymousConstructorTest { + class A1(using x1: Int) + + class A2(using x1: Int) { + def f: Int = x1 + } + + trait B1(using x1: Int) + + trait B2(using x1: Int) { + def f: Int = x1 + } +} diff --git a/output/src/main/scala-3/fix/UsingParamAnonymousConstructorTest.scala b/output/src/main/scala-3/fix/UsingParamAnonymousConstructorTest.scala new file mode 100644 index 0000000..d78875b --- /dev/null +++ b/output/src/main/scala-3/fix/UsingParamAnonymousConstructorTest.scala @@ -0,0 +1,15 @@ +package fix + +trait UsingParamAnonymousConstructorTest { + class A1(using Int) + + class A2(using x1: Int) { + def f: Int = x1 + } + + trait B1(using Int) + + trait B2(using x1: Int) { + def f: Int = x1 + } +} diff --git a/rules/src/main/scala/fix/UsingParamAnonymousConstructor.scala b/rules/src/main/scala/fix/UsingParamAnonymousConstructor.scala new file mode 100644 index 0000000..2936d23 --- /dev/null +++ b/rules/src/main/scala/fix/UsingParamAnonymousConstructor.scala @@ -0,0 +1,41 @@ +package fix + +import scala.meta.Mod +import scala.meta.Stat +import scala.meta.Term +import scala.meta.XtensionClassifiable +import scala.meta.XtensionCollectionLikeUI +import scala.meta.tokens.Token +import scalafix.Patch +import scalafix.v1.SyntacticDocument +import scalafix.v1.SyntacticRule +import scalafix.v1.XtensionSeqPatch + +class UsingParamAnonymousConstructor extends SyntacticRule("UsingParamAnonymousConstructor") { + override def fix(implicit doc: SyntacticDocument): Patch = { + doc.tree.collect { case c: Stat.WithCtor => + val t = c.ctor + t.paramClauses.filter { paramClause => + paramClause.values.forall(_.mods.exists(_.is[Mod.Using])) && paramClause.values.forall(m => + !m.mods.exists(_.is[Mod.Inline]) && !m.mods.exists(_.is[Mod.Annot]) + ) + }.map { paramClause => + val countOneNames = c.collect { case x: Term.Name => + x.value + }.groupBy(identity).filter(_._2.size == 1).keySet + if (paramClause.values.forall(a => countOneNames(a.name.value))) { + paramClause.values + .map(p => + Seq( + Patch.removeTokens(p.name.tokens), + Patch.removeToken(p.tokens.find(_.is[Token.Colon]).get) + ).asPatch + ) + .asPatch + } else { + Patch.empty + } + }.asPatch + }.asPatch + } +}