Skip to content

Commit 3979797

Browse files
committed
Add a TypelevelMTLSubmarine rule
1 parent e138375 commit 3979797

File tree

9 files changed

+1436
-5
lines changed

9 files changed

+1436
-5
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ jobs:
7474

7575
- name: Make target directories
7676
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
77-
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
77+
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
7878

7979
- name: Compress target directories
8080
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
81-
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
81+
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
8282

8383
- name: Upload target directories
8484
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
@@ -195,7 +195,7 @@ jobs:
195195
- name: Submit Dependencies
196196
uses: scalacenter/sbt-dependency-submission@v2
197197
with:
198-
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
198+
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
199199
configs-ignore: test scala-tool scala-doc-tool test-internal
200200

201201
validate-steward:

.scalafmt.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ indent {
1111
defnSite = 2
1212
extendSite = 2
1313
}
14+
15+
fileOverride {
16+
"glob:**/scala-3/**" {
17+
runner.dialect = scala3
18+
}
19+
}

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix" % "0
2121
ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix-cats" % "0.2.0"
2222
// To add only cats-effect Scalafix rules
2323
ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix-cats-effect" % "0.2.0"
24+
// To add only cats-mtl Scalafix rules
25+
ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix-cats-mtl" % "0.2.0"
2426
// To add only fs2 Scalafix rules
2527
ThisBuild / scalafixDependencies += "org.typelevel" %% "typelevel-scalafix-fs2" % "0.2.0"
2628
// To add only http4s Scalafix rules
@@ -41,6 +43,7 @@ rules = [
4143
TypelevelFs2SyncCompiler
4244
TypelevelHttp4sLiteralsSyntax
4345
TypelevelIORandomUUID
46+
TypelevelMTLSubmarine
4447
]
4548
```
4649

@@ -62,6 +65,7 @@ Not all rules function with Scala 3 yet.
6265
| TypelevelUnusedShowInterpolator | :white_check_mark: | :x: |
6366
| TypelevelFs2SyncCompiler | :white_check_mark: | :x: |
6467
| TypelevelHttp4sLiteralsSyntax | :white_check_mark: | :white_check_mark: |
68+
| TypelevelMTLSubmarine | :white_check_mark: | :white_check_mark: |
6569

6670
## Rules for cats
6771

@@ -170,6 +174,36 @@ val test = IO.randomUUID
170174

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

177+
## Rules for cats-mtl
178+
179+
### TypelevelMTLSubmarine
180+
181+
See https://typelevel.org/blog/2025/09/02/custom-error-types.html.
182+
183+
This rule forbids calling error-handling methods (`handleError`, `recover`, `onError`, etc.)
184+
on expressions that require `cats.mtl.Raise[F, E]`.
185+
`Raise` provided by `Handle.allow` uses a traceless exception type called `cats.mtl.Handle#Submarine`,
186+
so handling it through `ApplicativeError`, `MonadError`, or `IO` error methods might lead to unexpected results.
187+
188+
For example:
189+
```scala
190+
import cats.effect.IO
191+
import cats.mtl.{Raise, Handle}
192+
193+
def raiseError: Raise[IO, String] ?=> IO[Unit] = r =>
194+
r.raise("boom")
195+
196+
def standardError: IO[Unit] =
197+
IO.raiseError(new RuntimeException("boom"))
198+
199+
Handle.allow[String] {
200+
for {
201+
_ <- raiseError.onError(e => IO.println("Error: " + e)) // forbidden
202+
_ <- standardError.onError(e => IO.println("Error: " + e)) // allowed
203+
} yield
204+
}
205+
```
206+
173207
## Rules for fs2
174208

175209
### TypelevelFs2SyncCompiler

build.sbt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ lazy val CatsVersion = "2.12.0"
77
lazy val CatsEffectVersion = "3.6.3"
88
lazy val Fs2Version = "3.12.2"
99
lazy val Http4sVersion = "0.23.32"
10+
lazy val MtlVersion = "1.6.0"
1011

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

2021
lazy val `typelevel-scalafix` = project
2122
.in(file("."))
22-
.aggregate(`typelevel-scalafix-rules`, cats.all, catsEffect.all, fs2.all, http4s.all)
23+
.aggregate(`typelevel-scalafix-rules`, cats.all, catsEffect.all, fs2.all, http4s.all, mtl.all)
2324
.enablePlugins(NoPublishPlugin)
2425

2526
lazy val `typelevel-scalafix-rules` = project
2627
.in(file("target/rules-aggregate"))
27-
.dependsOn(cats.rules, catsEffect.rules, fs2.rules, http4s.rules)
28+
.dependsOn(cats.rules, catsEffect.rules, fs2.rules, http4s.rules, mtl.rules)
2829
.settings(
2930
moduleName := "typelevel-scalafix",
3031
tlVersionIntroduced ++= List("2.12", "2.13").map(_ -> "0.1.2").toMap,
@@ -83,3 +84,17 @@ lazy val http4s = scalafixProject("http4s")
8384
"org.http4s" %% "http4s-core" % Http4sVersion
8485
)
8586
)
87+
88+
// typelevel/mtl Scalafix rules
89+
lazy val mtl = scalafixProject("mtl")
90+
.rulesSettings(
91+
tlVersionIntroduced ++= List("2.12", "2.13").map(_ -> "0.6.0").toMap
92+
)
93+
.inputSettings(
94+
scalaVersion := "3.7.2",
95+
crossScalaVersions := Seq(V.scala213, "3.7.2"),
96+
libraryDependencies ++= Seq(
97+
"org.typelevel" %% "cats-effect" % CatsEffectVersion,
98+
"org.typelevel" %% "cats-mtl" % MtlVersion
99+
)
100+
)

0 commit comments

Comments
 (0)