{"action":"created","issue":{"url":"https://api.github.com/repos/sksamuel/scalac-scapegoat-plugin/issues/118","labels_url":"https://api.github.com/repos/sksamuel/scalac-scapegoat-plugin/issues/118/labels{/name}","comments_url":"https://api.github.com/repos/sksamuel/scalac-scapegoat-plugin/issues/118/comments","events_url":"https://api.github.com/repos/sksamuel/scalac-scapegoat-plugin/issues/118/events","html_url":"https://github.com/sksamuel/scalac-scapegoat-plugin/issues/118","id":90205955,"number":118,"title":"False positive / possible scalac issue? Case object with same name as abstract class.","user":{"login":"RichardBradley","id":1265780,"avatar_url":"https://avatars.githubusercontent.com/u/1265780?v=3","gravatar_id":"","url":"https://api.github.com/users/RichardBradley","html_url":"https://github.com/RichardBradley","followers_url":"https://api.github.com/users/RichardBradley/followers","following_url":"https://api.github.com/users/RichardBradley/following{/other_user}","gists_url":"https://api.github.com/users/RichardBradley/gists{/gist_id}","starred_url":"https://api.github.com/users/RichardBradley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RichardBradley/subscriptions","organizations_url":"https://api.github.com/users/RichardBradley/orgs","repos_url":"https://api.github.com/users/RichardBradley/repos","events_url":"https://api.github.com/users/RichardBradley/events{/privacy}","received_events_url":"https://api.github.com/users/RichardBradley/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":3,"created_at":"2015-06-22T20:33:52Z","updated_at":"2015-06-24T10:42:00Z","closed_at":null,"body":"Akka has some case objects with the same name as an adjacent abstract class (which they extend). This is rather odd, but I think it's for Java compat. (e.g. [\"ActorPublisherMessage.Cancel\"](https://github.com/akka/akka/blob/a9d6e439294aee60af7c36cc51ccfc9ba32dc6be/akka-stream/src/main/scala/akka/stream/actor/ActorPublisher.scala#L54))\r\n\r\nThis confuses the \"SuspiciousMatchOnClassObject\" inspection, but it also seems to produce an incorrect \"match may not be exhaustive\" warning from scalac.\r\n\r\nCan you work out what's going on here?\r\n\r\nHere's a failing test for scapegoat:\r\n\r\n```\r\nscala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObjectTest.scala\r\nindex c72e3b8..da88e7b 100644\r\n--- a/src/test/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObjectTest.scala\r\n+++ b/src/test/scala/com/sksamuel/scapegoat/inspections/matching/SuspiciousMatchOnClassObjectTest.scala\r\n@@ -152,6 +152,32 @@ class SuspiciousMatchOnClassObjectTest\r\n         compileCodeSnippet(code)\r\n         compiler.scapegoat.feedback.warnings.size shouldBe 0\r\n       }\r\n+\r\n+      \"for case objects with adjacent classes of the same name\" in {\r\n+        val code = \"\"\"sealed abstract class ActorPublisherMessage\r\n+                     |\r\n+                     |object ActorPublisherMessage {\r\n+                     |  final case object Cancel extends Cancel\r\n+                     |  sealed class Cancel extends ActorPublisherMessage\r\n+                     |\r\n+                     |  final case object SubscriptionTimeoutExceeded extends SubscriptionTimeoutExceeded\r\n+                     |  sealed abstract class SubscriptionTimeoutExceeded extends ActorPublisherMessage\r\n+                     |}\r\n+                     |\r\n+                     |object A {\r\n+                     |  def handle(a: ActorPublisherMessage) = {\r\n+                     |    a match {\r\n+                     |      case ActorPublisherMessage.Cancel =>\r\n+                     |        println(\"x\")\r\n+                     |      case ActorPublisherMessage.SubscriptionTimeoutExceeded =>\r\n+                     |        println(\"y\")\r\n+                     |    }\r\n+                     |  }\r\n+                     |}\"\"\".stripMargin\r\n+        compileCodeSnippet(code)\r\n+        compiler.scapegoat.feedback.warnings.size shouldBe 0\r\n+      }\r\n+\r\n       \"for instances of case class\" in {\r\n\r\n```\r\n\r\nThe match above is given a warning by scalac:\r\n```\r\nwarning: match may not be exhaustive.\r\nIt would fail on the following input: Cancel()\r\n    a match {\r\n    ^\r\n```\r\n\r\n... but this warning (and scapegoat's warning) is incorrect, as the match will actually catch \"Cancel\" objects and isn't matching on the class name (also \"Cancel\").\r\n\r\nIf you run the following in `scala`, you'll see that the \"case Cancel\" is being interpreted as a literal \"==\" match on the case object, and not for the type \"Cancel\", so the warning from `scalac` is incorrect:\r\n\r\n```\r\nscala> sealed abstract class ActorPublisherMessage\r\ndefined class ActorPublisherMessage\r\n\r\nscala> object ActorPublisherMessage {\r\n     |   final case object Cancel extends Cancel\r\n     |   sealed class Cancel extends ActorPublisherMessage\r\n     |\r\n     |   final case object SubscriptionTimeoutExceeded extends SubscriptionTimeoutExceeded\r\n     |   sealed abstract class SubscriptionTimeoutExceeded extends ActorPublisherMessage\r\n     | }\r\ndefined object ActorPublisherMessage\r\nwarning: previously defined class ActorPublisherMessage is not a companion to object ActorPublisherMessage.\r\nCompanions must be defined together; you may wish to use :paste mode for this.\r\n\r\nscala>\r\n\r\nscala> object A {\r\n     |   def handle(a: ActorPublisherMessage) = {\r\n     |     a match {\r\n     |       case ActorPublisherMessage.Cancel =>\r\n     |         println(\"x\")\r\n     |       case ActorPublisherMessage.SubscriptionTimeoutExceeded =>\r\n     |         println(\"y\")\r\n     |     }\r\n     |   }\r\n     | }\r\n<console>:11: warning: match may not be exhaustive.\r\nIt would fail on the following input: Cancel()\r\n           a match {\r\n           ^\r\ndefined object A\r\n\r\nscala> val msg: ActorPublisherMessage = ActorPublisherMessage.Cancel\r\nmsg: ActorPublisherMessage = Cancel\r\n\r\nscala> A.handle(msg)\r\nx\r\n```\r\n\r\n\r\n1. Do you agree that the warning from scalac is incorrect? Or have I misunderstood it?\r\n2. Do you think that this is a legal pattern match? I can't decide what the [language spec](http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html) declares here. Does \"case Cancel\" refer to the type or the case object according to the spec?\r\n3. Do you think that scalac is changing whether \"case Cancel\" refers to the type or the case object from one compile phase to the next, and that might be why the warning is issued by scalac and scapegoat but the match does then work correctly?\r\n4. WTH is going on here? Should I send this to the scala mailing list? To the Akka mailing list?\r\n\r\nThanks,\r\n\r\n\r\nRich\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/sksamuel/scalac-scapegoat-plugin/issues/comments/114820081","html_url":"https://github.com/sksamuel/scalac-scapegoat-plugin/issues/118#issuecomment-114820081","issue_url":"https://api.github.com/repos/sksamuel/scalac-scapegoat-plugin/issues/118","id":114820081,"user":{"login":"RichardBradley","id":1265780,"avatar_url":"https://avatars.githubusercontent.com/u/1265780?v=3","gravatar_id":"","url":"https://api.github.com/users/RichardBradley","html_url":"https://github.com/RichardBradley","followers_url":"https://api.github.com/users/RichardBradley/followers","following_url":"https://api.github.com/users/RichardBradley/following{/other_user}","gists_url":"https://api.github.com/users/RichardBradley/gists{/gist_id}","starred_url":"https://api.github.com/users/RichardBradley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RichardBradley/subscriptions","organizations_url":"https://api.github.com/users/RichardBradley/orgs","repos_url":"https://api.github.com/users/RichardBradley/repos","events_url":"https://api.github.com/users/RichardBradley/events{/privacy}","received_events_url":"https://api.github.com/users/RichardBradley/received_events","type":"User","site_admin":false},"created_at":"2015-06-24T10:42:00Z","updated_at":"2015-06-24T10:42:00Z","body":"Oh, hang on.\r\n\r\nThis isn't some mysterious problem; it's just that the \"sealed class Cancel extends ActorPublisherMessage\" is missing an \"abstract\" modifier.\r\n\r\nThat makes the \"match\" total above.\r\n\r\nI'll raise an Akka PR.\r\n\r\nI'll clarify if this is a real scapegoat issue (I think it is). Maybe a new issue is called for to sweep away this distraction :-)"}}