Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p modules/fs2/rules/target target/rules-aggregate/target modules/cats-effect/rules/target modules/http4s/rules/target modules/cats/rules/target project/target
run: mkdir -p modules/fs2/rules/target target/rules-aggregate/target modules/cats-effect/rules/target modules/http4s/rules/target modules/mtl/rules/target modules/cats/rules/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar modules/fs2/rules/target target/rules-aggregate/target modules/cats-effect/rules/target modules/http4s/rules/target modules/cats/rules/target project/target
run: tar cf targets.tar modules/fs2/rules/target target/rules-aggregate/target modules/cats-effect/rules/target modules/http4s/rules/target modules/mtl/rules/target modules/cats/rules/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down Expand Up @@ -195,7 +195,7 @@ jobs:
- name: Submit Dependencies
uses: scalacenter/sbt-dependency-submission@v2
with:
modules-ignore: cats-output_2.13 cats-output_2.12 http4s_2.13 http4s_2.12 fs2_2.13 fs2_2.12 cats-effect_2.13 cats-effect_2.12 cats-tests_2.13 cats-tests_2.12 fs2-tests_2.13 fs2-tests_2.12 cats-effect-tests_2.13 cats-effect-tests_2.12 typelevel-scalafix_2.13 typelevel-scalafix_2.12 fs2-output_2.13 fs2-output_2.12 fs2-input_2.13 fs2-input_2.12 cats-effect-input_2.13 cats-effect-input_2.12 http4s-tests_2.13 http4s-tests_2.12 http4s-input_2.13 http4s-input_2.12 cats-effect-output_2.13 cats-effect-output_2.12 http4s-output_2.13 http4s-output_2.12 cats_2.13 cats_2.12 cats-input_2.13 cats-input_2.12
modules-ignore: mtl-output_2.13 mtl-output_2.12 cats-output_2.13 cats-output_2.12 http4s_2.13 http4s_2.12 fs2_2.13 fs2_2.12 mtl-tests_2.13 mtl-tests_2.12 cats-effect_2.13 cats-effect_2.12 cats-tests_2.13 cats-tests_2.12 fs2-tests_2.13 fs2-tests_2.12 cats-effect-tests_2.13 cats-effect-tests_2.12 typelevel-scalafix_2.13 typelevel-scalafix_2.12 fs2-output_2.13 fs2-output_2.12 fs2-input_2.13 fs2-input_2.12 cats-effect-input_2.13 cats-effect-input_2.12 http4s-tests_2.13 http4s-tests_2.12 mtl-input_2.13 mtl-input_3 http4s-input_2.13 http4s-input_2.12 mtl_2.13 mtl_2.12 cats-effect-output_2.13 cats-effect-output_2.12 http4s-output_2.13 http4s-output_2.12 cats_2.13 cats_2.12 cats-input_2.13 cats-input_2.12
configs-ignore: test scala-tool scala-doc-tool test-internal

validate-steward:
Expand Down
6 changes: 6 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ indent {
defnSite = 2
extendSite = 2
}

fileOverride {
"glob:**/scala-3/**" {
runner.dialect = scala3
}
}
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix" % "0
ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix-cats" % "0.2.0"
// To add only cats-effect Scalafix rules
ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix-cats-effect" % "0.2.0"
// To add only cats-mtl Scalafix rules
ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix-cats-mtl" % "0.2.0"
// To add only fs2 Scalafix rules
ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix-fs2" % "0.2.0"
// To add only http4s Scalafix rules
Expand All @@ -41,6 +43,7 @@ rules = [
TypelevelFs2SyncCompiler
TypelevelHttp4sLiteralsSyntax
TypelevelIORandomUUID
TypelevelMTLSubmarine
]
```

Expand All @@ -62,6 +65,7 @@ Not all rules function with Scala 3 yet.
| TypelevelUnusedShowInterpolator | :white_check_mark: | :x: |
| TypelevelFs2SyncCompiler | :white_check_mark: | :x: |
| TypelevelHttp4sLiteralsSyntax | :white_check_mark: | :white_check_mark: |
| TypelevelMTLSubmarine | :white_check_mark: | :white_check_mark: |

## Rules for cats

Expand Down Expand Up @@ -170,6 +174,36 @@ val test = IO.randomUUID

This rule works on variable declarations, usaged within methods as well as for comprehensions.

## Rules for cats-mtl

### TypelevelMTLSubmarine

See https://typelevel.org/blog/2025/09/02/custom-error-types.html.

This rule forbids calling error-handling methods (`handleError`, `recover`, `onError`, etc.)
on expressions that require `cats.mtl.Raise[F, E]`.
`Raise` provided by `Handle.allow` uses a traceless exception type called `cats.mtl.Handle#Submarine`,
so handling it through `ApplicativeError`, `MonadError`, or `IO` error-handling methods is not always desirable.

For example:
```scala
import cats.effect.IO
import cats.mtl.{Raise, Handle}

def raiseError: Raise[IO, String] ?=> IO[Unit] = r =>
r.raise("boom")

def standardError: IO[Unit] =
IO.raiseError(new RuntimeException("boom"))

Handle.allow[String] {
for {
_ <- raiseError.onError(e => IO.println("Error: " + e)) // forbidden
_ <- standardError.onError(e => IO.println("Error: " + e)) // allowed
} yield
}
```

## Rules for fs2

### TypelevelFs2SyncCompiler
Expand Down
19 changes: 17 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ lazy val CatsVersion = "2.12.0"
lazy val CatsEffectVersion = "3.6.3"
lazy val Fs2Version = "3.12.2"
lazy val Http4sVersion = "0.23.32"
lazy val MtlVersion = "1.6.0"

ThisBuild / startYear := Some(2022)
ThisBuild / developers ++= List(
Expand All @@ -19,12 +20,12 @@ ThisBuild / scalafixScalaBinaryVersion := CrossVersion.binaryScalaVersion(scalaV

lazy val `typelevel-scalafix` = project
.in(file("."))
.aggregate(`typelevel-scalafix-rules`, cats.all, catsEffect.all, fs2.all, http4s.all)
.aggregate(`typelevel-scalafix-rules`, cats.all, catsEffect.all, fs2.all, http4s.all, mtl.all)
.enablePlugins(NoPublishPlugin)

lazy val `typelevel-scalafix-rules` = project
.in(file("target/rules-aggregate"))
.dependsOn(cats.rules, catsEffect.rules, fs2.rules, http4s.rules)
.dependsOn(cats.rules, catsEffect.rules, fs2.rules, http4s.rules, mtl.rules)
.settings(
moduleName := "typelevel-scalafix",
tlVersionIntroduced ++= List("2.12", "2.13").map(_ -> "0.1.2").toMap,
Expand Down Expand Up @@ -83,3 +84,17 @@ lazy val http4s = scalafixProject("http4s")
"org.http4s" %% "http4s-core" % Http4sVersion
)
)

// typelevel/mtl Scalafix rules
lazy val mtl = scalafixProject("mtl")
.rulesSettings(
tlVersionIntroduced ++= List("2.12", "2.13").map(_ -> "0.6.0").toMap
)
.inputSettings(
scalaVersion := "3.7.2",
crossScalaVersions := Seq(V.scala213, "3.7.2"),
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect" % CatsEffectVersion,
"org.typelevel" %% "cats-mtl" % MtlVersion
)
)
Loading