diff --git a/input/src/main/scala/fix/UnreachableCatch.scala b/input/src/main/scala/fix/UnreachableCatch.scala new file mode 100644 index 0000000..af9c5ca --- /dev/null +++ b/input/src/main/scala/fix/UnreachableCatch.scala @@ -0,0 +1,46 @@ +/* +rule = UnreachableCatch + */ +package fix + +import scala.annotation.nowarn + +object UnreachableCatch { + + @nowarn // Some unreachable warning may appear thanks to Scala built-in warnings. + // Since we are testing the rule, we can ignore them. + def test(): Unit = { + try { + } catch { + case _ : Throwable => // scalafix: ok; + case e : Exception => // assert: UnreachableCatch + } + + // Test case 2 with unreachability due to guard + try { + } catch { + case e: RuntimeException => // scalafix: ok; + case e: RuntimeException if e.getMessage.contains("foo") => // assert: UnreachableCatch + } + + try { + } catch { + case e : RuntimeException => // scalafix: ok; + case f : Exception => // scalafix: ok; + case x : Throwable => // scalafix: ok; + } + + try { + } catch { + case e: RuntimeException if e.getMessage.contains("foo") => // scalafix: ok; + case e: RuntimeException => // scalafix: ok; + } + + try { + } catch { + case e: RuntimeException if e.getMessage.contains("foo") => // scalafix: ok; + case e: RuntimeException if e.getMessage.contains("bar") => // scalafix: ok; + } + } + +} diff --git a/rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/rules/src/main/resources/META-INF/services/scalafix.v1.Rule index 12de0a8..efc3a35 100644 --- a/rules/src/main/resources/META-INF/services/scalafix.v1.Rule +++ b/rules/src/main/resources/META-INF/services/scalafix.v1.Rule @@ -41,4 +41,5 @@ fix.NullAssignment fix.RepeatedCaseBody fix.RepeatedIfElseBody fix.SwallowedException -fix.UnnecessaryConversion \ No newline at end of file +fix.UnnecessaryConversion +fix.UnreachableCatch \ No newline at end of file diff --git a/rules/src/main/scala/fix/UnreachableCatch.scala b/rules/src/main/scala/fix/UnreachableCatch.scala new file mode 100644 index 0000000..9b3cf9d --- /dev/null +++ b/rules/src/main/scala/fix/UnreachableCatch.scala @@ -0,0 +1,44 @@ +/* +rule = UnreachableCatch + */ +package fix + +import scalafix.lint.LintSeverity +import scalafix.v1._ + +import scala.collection.mutable +import scala.meta._ + +class UnreachableCatch extends SemanticRule("UnreachableCatch") { + + private def diag(pos: Position) = Diagnostic( + "", + "Checks for catch clauses that cannot be reached.", + pos, + "One or more cases are unreachable.", + LintSeverity.Warning + ) + + + override def fix(implicit doc: SemanticDocument): Patch = { + + doc.tree.collect { + case Term.Try(_, cases, _) => + val types = mutable.HashSet[String]() + cases.collect { + case c @ Case(Pat.Typed(_, tpe), guard, _) => + if(types.exists(t => Util.inheritsFrom(tpe.symbol, t))) Patch.lint(diag(c.pos)) + else { + tpe.symbol.info.foreach(_.signature match { + case TypeSignature(_, _, TypeRef(_, symbol, _)) if guard.isEmpty => types.add(symbol.value) // Extract upperbound symbol of type + // We only add it if there is no guard because if we have twice the same type they trivially inherit from each other. + // By adding only if there is no guard, we will flag later ones with guards as unreachable, because they will be. See test case 2 + case _ => () + }) + Patch.empty + } + case _ => Patch.empty + } + }.flatten.asPatch + } +}