Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

False positive unreachable code warning with sealed abstract class #11457

Open
rossabaker opened this issue Mar 29, 2019 · 14 comments
Open

False positive unreachable code warning with sealed abstract class #11457

rossabaker opened this issue Mar 29, 2019 · 14 comments

Comments

@rossabaker
Copy link

I will minimize this after Typelevel Summit, but filing a placeholder now.

  • Scala version: 2.13.0-M5
  • Java version: 1.8.0_151

In this snippet, the Ok(req.body) is falsely flagged as unreachable code.

  val testService = HttpRoutes.of[IO] {
    case GET -> Root / "request" =>
      Ok("request response")
    case req @ POST -> Root / "post" =>
      Ok(req.body)
  }

We have many similar warnings in the tests using this DSL. No warning is generated in Scala 2.12.8 or 2.11.12.

First spotted in http4s/http4s#2393.

@SethTisue
Copy link
Member

"good first issue" because a newcomer could at least attempt to isolate it. (and once isolated, perhaps a fix would be within reach, too.)

@rossabaker
Copy link
Author

Sorry, I've failed to come back to minimize this, but 👍 to the good-first-issue. If anyone needs help with the mechanics of building http4s or a tutorial on what that code is doing, I'm happy to help here or on Gitter.

@aeons
Copy link

aeons commented Jun 13, 2019

This is still present in 2.13.0.

@rossabaker rossabaker changed the title False positive unreachable code warning in http4s-dsl False positive unreachable code warning with sealed abstract class Jun 17, 2019
@rossabaker
Copy link
Author

rossabaker commented Jun 17, 2019

I minimized this to sealed abstract case classes. This warns on false:

sealed abstract class Foo(val a: String)

object Foo {
  def unapply(foo: Foo): Some[String] =
    Some(foo.a)
}

class Issue11457 {
  val root: PartialFunction[Foo, Boolean] = {
    case Foo("a") => true
    case Foo("b") => false
  }
}

If Foo is an unsealed abstract class or a concrete class, there is no warning.

@Igosuki
Copy link

Igosuki commented Jun 20, 2019

Got this when trying to port http4s/rho to 2.13 with the following code :

 def routes(implicit cs: ContextShift[IO]): HttpRoutes[IO] = HttpRoutes.of[IO] {
    // Swagger User Interface
    case req @ GET -> Root / "css" / _       => fetchResource(swaggerUiDir + req.pathInfo, req)
    case req @ GET -> Root / "images" / _    => fetchResource(swaggerUiDir + req.pathInfo, req)
    case req @ GET -> Root / "lib" / _       => fetchResource(swaggerUiDir + req.pathInfo, req)
    case req @ GET -> Root / "swagger-ui"    => fetchResource(swaggerUiDir + "/index.html", req)
    case req @ GET -> Root / "swagger-ui.js" => fetchResource(swaggerUiDir + "/swagger-ui.min.js", req)
  }

@lrytz lrytz modified the milestones: Backlog, 2.13.1 Jun 24, 2019
@lrytz
Copy link
Member

lrytz commented Jun 24, 2019

Moved to 2.13.1 since this warning is emitted by default, without special compiler flags.

@lrytz
Copy link
Member

lrytz commented Jul 26, 2019

I worked on a fix for the minimization, but unfortunately that doesn't fix the original issue in the http4s repo.

The minimization did regress between M4 and M5, in https://github.com/scala/scala/pull/6485/files. I'm not sure if the http4s example also regressed with that change, but it seems likely.

What seems special in the minimization: we are matching on a sealed abstract class with no subclasses MatchAnalysis.scala#L144-L154. Since sealed abstract types are excluded (see scala/scala@311b7de861), the domain in the patmat analyis will be empty Logic.scala#L547; I'm not sure if this violates a precondition. The following WIP fixes the minimization, but as I said earlier not the http4s example. https://github.com/scala/scala/compare/2.13.x...lrytz:t11457?expand=1

@eed3si9n do you maybe have time to take a look?

@szeiger szeiger modified the milestones: 2.13.1, 2.13.2 Sep 9, 2019
@leonardehrenfried
Copy link

I have just hit this issue when migrating our service to scala 2.13.

Is there a workaround to make the code compile even when turning warnings into errors?

@eed3si9n
Copy link
Member

do you maybe have time to take a look?

sorry, I somehow missed this.

Is there a workaround to make the code compile even when turning warnings into errors?

There's a planed feature @lrytz is working on to help there, but in the meantime maybe try silencer? - https://github.com/ghik/silencer

@leonardehrenfried
Copy link

Thanks for the tip, @eed3si9n. I can make our code base compile with 2.13.

Since I want to disable the smallest possible number of warnings, does anyone know that the precise name of this warning is? https://docs.scala-lang.org/overviews/compiler-options/index.html

@SethTisue
Copy link
Member

SethTisue commented Dec 5, 2019

#11649 is a duplicate or near-duplicate; #10251, also

@julienrf
Copy link

I believe the following snippet is another way to exhibit the same bug: https://scastie.scala-lang.org/eSn923EqTFy4aIHEdp94sg

sealed trait Command {
  type Reply
}

final case class Create() extends Command {
  type Reply = CreateReply
  val reply: Reply = CreateReply()
}

final case class Set() extends Command {
  type Reply = SetReply
  val reply: Reply = SetReply()
}

case class CreateReply()
case class SetReply()

def process[R](command: Command { type Reply = R }): R =
  command match {
    case create: Create => create.reply
    case set: Set       => set.reply
//                         ^
// Warning: unreachable code
  }

@SethTisue SethTisue modified the milestones: 2.13.2, Backlog Feb 6, 2020
@SethTisue
Copy link
Member

SethTisue commented Feb 6, 2020

we haven't succeeded in nerd-sniping anyone into attempting a fix, it seems. but we have attempted to nerd-snipe @som-snytt yet?

"nerd-snipe som snytt yet": say it three times fast

@som-snytt
Copy link

I started looking at this and took it on a jog this afternoon. Returning by Coyote Trail, I came to the same conclusion as Lukas (because I'd failed to read his comment first), namely, an extractor never matches null (as I recall from the regex extractor); since a sealed abstract class with no subclasses (including anonymous) can have no instances, an extractor taking that type can never match.

However, in the minimization, just one case should fail to compile, as it must fail to match.

patmat is a nontrivial learning curve. I wasn't sure whether to look first at Scala 2 (as mature) or Scala 3 (as a better rewrite).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests