stanch / reftree

Automatically generated diagrams and animations for Scala data structures
http://stanch.github.io/reftree/
GNU General Public License v3.0
586 stars 36 forks source link

Internal macro error building reftree #19

Closed wsargent closed 5 days ago

wsargent commented 6 years ago

Exception building diagram (on scala 2.12.5)

[info] Compiling 1 Scala source to /home/wsargent/work/scala-capabilities/images/target/scala-2.12/classes ...
[error] /home/wsargent/work/scala-capabilities/images/src/main/scala/Main.scala:90:44: Internal error: unable to find the outer accessor symbol of class anon$Generic Coproduct RefTree$macro$14
[error]     val diagram = Diagram(nameChanger)
[error]                                            ^
[error] ## Exception when compiling 1 sources to /home/wsargent/work/scala-capabilities/images/target/scala-2.12/classes
[error] null
[error] java.lang.String.valueOf(String.java:2994)

code is as follows

import java.nio.file.{Path, Paths}

import reftree.diagram.Diagram
import reftree.render.{Renderer, RenderingOptions}

object Main {

  // #definition
  final class Document(private var name: String) {
    private object capabilities {
      val nameChanger = new Document.NameChanger {
        override def changeName(newName: String): Unit =  {
          name = newName
        }
      }
    }
    override def toString: String = s"Document($name)"
  }
  // #definition

  // #access
  object Document {
    sealed trait NameChanger {
      def changeName(name: String): Unit
    }

    // Policy controls who has access to what
    class Access private {
      def nameChanger(doc: Document): NameChanger = {
        doc.capabilities.nameChanger
      }
    }

    object Access {
      def apply(): Access = new Access
    }
  }
  // #access

  // https://www.youtube.com/watch?v=6mWaqGHeg3g

  def main(args: Array[String]): Unit = {
    val imagePath = Paths.get("images") // current relative path + "images"
    implicit val renderer = Renderer(
      renderingOptions = RenderingOptions(),
      directory = imagePath.resolve("overview")
    )

    import renderer._

    def render(name: String, diagram: Diagram): Path = {
      diagram.render(name)
      directory.resolve(s"$name.${renderer.format}")
    }

    def display(path: Path) = {
      println(s"Open with Browser ${path.toUri}")
      println(s"Open with IntelliJ IDEA http://localhost:63342/api/file/${path.toString}")
    }

    val document = new Document("will")
    val access = Document.Access()
    val nameChanger = access.nameChanger(document)
    println(s"result = $document")

    val diagram = Diagram(nameChanger)

    display(render("nameChanger", diagram))
  }
}
stanch commented 6 years ago

Looks like a bug/limitation of shapeless (reftree doesn’t have any macros of its own). But I am wondering how a diagram can be automatically derived for NameChanger. It’s a sealed trait with only a private anonymous subclass, and that subclass does not have any attributes. I don’t think shapeless can support that (see https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#generic-representation-of-sealed-families-of-case-classes), but if it did, what output would you expect?