Add catchingExceptions method to replace runCatching (#4797)

- Add `runCatchingExceptions` and `mapCatchingExceptions` to replace `runCatching` and `mapCatching`.
- Make `tryOrNull { ... }` catch only exceptions too.
- Apply the changes to the whole project.
- Add new Rust fakes for tests to handle the code that's now unblocked - previously it just threw an `UnsatisfiedLinkError` which we ignored.
- Add a new `detekt-rules` project with a `RunCatchingRule` to prevent `runCatching` and `mapCatching` usages.
This commit is contained in:
Jorge Martin Espinosa 2025-06-04 09:02:26 +02:00 committed by GitHub
parent 7816529fd7
commit efdc10e60a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
144 changed files with 716 additions and 375 deletions

1
tests/detekt-rules/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,20 @@
plugins {
alias(libs.plugins.kotlin.jvm)
}
java {
sourceCompatibility = Versions.javaVersion
targetCompatibility = Versions.javaVersion
}
kotlin {
jvmToolchain {
languageVersion = Versions.javaLanguageVersion
}
}
dependencies {
compileOnly(libs.test.detekt.api)
testImplementation(libs.test.detekt.test)
testImplementation(libs.test.truth)
}

View file

@ -0,0 +1,23 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.detektrules
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.RuleSet
import io.gitlab.arturbosch.detekt.api.RuleSetProvider
class ElementRuleSetProvider : RuleSetProvider {
override val ruleSetId: String = "ElementXRules"
override fun instance(config: Config): RuleSet = RuleSet(
id = ruleSetId,
rules = listOf(
RunCatchingRule(config),
)
)
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.detektrules
import io.gitlab.arturbosch.detekt.api.CodeSmell
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
class RunCatchingRule(config: Config) : Rule(config) {
override val issue: Issue = Issue(
id = "RunCatchingNotAllowed",
severity = Severity.Style,
description = "Avoid using `runCatching`, use `runCatchingExceptions` or `tryOrNull` instead. " +
"Avoid `mapCatching`, use `mapCatchingExceptions` instead.",
debt = Debt.FIVE_MINS,
)
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)
val callNameExpression = expression.getCallNameExpression() ?: return
val hasRunCatchingCall = callNameExpression.text == "runCatching"
val hasMapCatchingCall = callNameExpression.text == "mapCatching"
if (hasRunCatchingCall || hasMapCatchingCall) {
report(CodeSmell(
issue = issue,
entity = Entity.from(expression),
message = "Use `runCatchingExceptions` or `tryOrNull` instead of `runCatching`. Avoid `mapCatching`, use `mapCatchingExceptions` instead."
))
}
}
}

View file

@ -0,0 +1 @@
io.element.android.detektrules.ElementRuleSetProvider

View file

@ -0,0 +1,33 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.detektrules
import com.google.common.truth.Truth.assertThat
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.test.compileAndLint
import org.junit.Test
class RunCatchingRuleTest {
private val subject = RunCatchingRule(Config.empty)
@Test
fun `test RunCatchingRule`() {
val findings = subject.compileAndLint(code)
assertThat(findings).hasSize(3)
}
private val code = """
object Foo {
fun bar() {
runCatching {}
kotlin.runCatching {}
Result.success(true).mapCatching { false }
}
}
""".trimIndent()
}

View file

@ -7,19 +7,8 @@
package io.element.android.tests.testutils.lambda
import kotlin.system.exitProcess
fun lambdaError(
message: String = "This lambda should never be called."
): Nothing {
// Throwing an exception here is not enough, it can be caught.
// Instead exit the process to make sure the test fails.
// The error will be:
// "Could not stop all services."
// In this case, put a breakpoint here and run the test in debug mode to identify which lambda is failing.
System.err.println(message)
Thread.currentThread().stackTrace.forEach {
System.err.println(it)
}
exitProcess(1)
throw AssertionError(message)
}