diff --git a/build.sbt b/build.sbt index d3ca0a7..2695a95 100644 --- a/build.sbt +++ b/build.sbt @@ -13,9 +13,8 @@ ThisBuild / developers ++= List( tlGitHubDev("DavidGregory084", "David Gregory") ) -ThisBuild / semanticdbEnabled := true -ThisBuild / semanticdbVersion := scalafixSemanticdb.revision -ThisBuild / scalafixScalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaVersion.value) +ThisBuild / semanticdbEnabled := true +ThisBuild / semanticdbVersion := scalafixSemanticdb.revision lazy val `typelevel-scalafix` = project .in(file(".")) @@ -39,6 +38,11 @@ lazy val cats = scalafixProject("cats") "org.typelevel" %% "cats-core" % CatsVersion ) ) + .outputSettings( + libraryDependencies ++= Seq( + "org.typelevel" %% "cats-core" % CatsVersion + ) + ) // typelevel/cats-effect Scalafix rules lazy val catsEffect = scalafixProject("cats-effect") diff --git a/modules/cats/input/src/main/scala/fix/EitherSyntax.scala b/modules/cats/input/src/main/scala/fix/EitherSyntax.scala new file mode 100644 index 0000000..142a0df --- /dev/null +++ b/modules/cats/input/src/main/scala/fix/EitherSyntax.scala @@ -0,0 +1,16 @@ +/* +rule = TypelevelEitherSyntax + */ +package fix + +object EitherSyntax { + + def shouldBeReplaced = { + Right(()) + } + + def shouldBeIgnored = { + Right(1) + } + +} diff --git a/modules/cats/output/src/main/scala/fix/EitherSyntax.scala b/modules/cats/output/src/main/scala/fix/EitherSyntax.scala new file mode 100644 index 0000000..bc9aee9 --- /dev/null +++ b/modules/cats/output/src/main/scala/fix/EitherSyntax.scala @@ -0,0 +1,14 @@ +package fix + +import cats.syntax.either._ +object EitherSyntax { + + def shouldBeReplaced = { + Either.unit + } + + def shouldBeIgnored = { + Right(1) + } + +} diff --git a/modules/cats/rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/modules/cats/rules/src/main/resources/META-INF/services/scalafix.v1.Rule index d1b5ca0..c8079d8 100644 --- a/modules/cats/rules/src/main/resources/META-INF/services/scalafix.v1.Rule +++ b/modules/cats/rules/src/main/resources/META-INF/services/scalafix.v1.Rule @@ -1,3 +1,4 @@ org.typelevel.fix.MapSequence org.typelevel.fix.UnusedShowInterpolator org.typelevel.fix.As +org.typelevel.fix.EitherSyntax diff --git a/modules/cats/rules/src/main/scala/org/typelevel/fix/EitherSyntax.scala b/modules/cats/rules/src/main/scala/org/typelevel/fix/EitherSyntax.scala new file mode 100644 index 0000000..69aac6e --- /dev/null +++ b/modules/cats/rules/src/main/scala/org/typelevel/fix/EitherSyntax.scala @@ -0,0 +1,99 @@ +/* + * Copyright 2022 Typelevel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.typelevel.fix + +import scalafix.v1._ +import scala.meta._ +import scala.collection.mutable.ListBuffer + +class EitherSyntax extends SemanticRule("TypelevelEitherSyntax") { + + override def fix(implicit doc: SemanticDocument): Patch = { + var docContainsImports: Boolean = false + val patches: ListBuffer[Patch] = ListBuffer.empty[Patch] + + doc.tree.traverse { + case ImportCatsSyntaxAll(_) => + docContainsImports = true + case ImportCatsSyntaxEither(_) => + docContainsImports = true + case expr @ Term.Apply.After_4_6_0(Term.Name("Right"), Term.ArgClause(List(Lit.Unit()), _)) => + if (!docContainsImports) + patches += Patch.addGlobalImport( + Importer( + Term.Select( + Term.Select(Term.Name("cats"), Term.Name("syntax")), + Term.Name("either") + ), + List(Importee.Wildcard()) + ) + ) + + patches += Patch.replaceTree(expr, "Either.unit") + } + + patches.asPatch + } +} + +object ImportCatsSyntaxAll { + def unapply(t: Tree): Option[String] = t match { + case Import( + List( + Importer( + Term.Select( + Term.Select(Term.Name("cats"), Term.Name("syntax")), + Term.Name("all") + ), + List(Importee.Wildcard()) + ) + ) + ) => + Some("cats.syntax.all._") + case _ => None + } +} + +object ImportCatsSyntaxEither { + def unapply(t: Tree): Option[String] = t match { + case Import( + List( + Importer( + Term.Select( + Term.Select(Term.Name("cats"), Term.Name("syntax")), + Term.Name("either") + ), + importee + ) + ) + ) => + if ( + importee.exists { + case Importee.Wildcard() => true + case _ => false + } + ) Some("cats.syntax.either._") + else if ( + importee.exists { + case Importee.Name(Name.Indeterminate("catsSyntaxEitherObject")) => true + case _ => false + } + ) Some("cats.syntax.either.catsSyntaxEitherObject") + else None + case _ => None + } +}