2

I have an object with some apply method defined and then use it

object Ob {
  def apply(i: Int) = ()
  def apply(s: String) = ()
}

object Use {
  def someMethod(i: Int) = ()

  Ob(1)
  someMethod(1)
}

When using scalafix/scalameta, I'm unable to find a way to get the handle of the actual apply method (In my case I'm trying to inspect the argument names and type for a scalafix rule)

When I match and print the resolved SymbolInformation, I get a reference to the object. So this

import scalafix.v1._
import scala.meta._

class NamedLiteralArguments extends SemanticRule("NamedLiteralArguments") {
  val minParam = 2

  override def fix(implicit doc: SemanticDocument): Patch = {
    doc.tree
      .collect {
        case Term.Apply(fun, args) =>
          println(fun.symbol.info)

          Patch.empty
      }
  }

prints

Some(test/Ob. => final object Ob extends AnyRef { +2 decls })
Some(test/Use.someMethod(). => method someMethod(i: Int): Unit)

But I want it to resolve the exact apply method instead.

(Scalafix version 0.9.20)

1 Answer 1

1

Switch on "-P:semanticdb:synthetics:on" in build.sbt

scalacOptions ++= List(
  "-Yrangepos",
  "-P:semanticdb:synthetics:on",
)

Then

import scalafix.v1._
import scala.meta._

class MyRule extends SemanticRule("MyRule") {
  override def fix(implicit doc: SemanticDocument): Patch = {
    doc.tree
      .collect {
        case t: Term =>
          println(s"t=$t=${t.structure}, t.symbol.info=${t.symbol.info}, t.synthetics=${t.synthetics.map(_.symbol.map(_.info))}")

          Patch.empty
      }.asPatch
  }
}

prints

t=Ob=Term.Name("Ob"), t.symbol.info=Some(_empty_/Ob. => final object Ob extends AnyRef { +2 decls }), t.synthetics=List()
t=apply=Term.Name("apply"), t.symbol.info=Some(_empty_/Ob.apply(). => method apply(i: Int): Unit), t.synthetics=List()
t=i=Term.Name("i"), t.symbol.info=Some(_empty_/Ob.apply().(i) => param i: Int), t.synthetics=List()
t=()=Lit.Unit, t.symbol.info=None, t.synthetics=List()
t=apply=Term.Name("apply"), t.symbol.info=Some(_empty_/Ob.apply(+1). => method apply(s: String): Unit), t.synthetics=List()
t=s=Term.Name("s"), t.symbol.info=Some(_empty_/Ob.apply(+1).(s) => param s: String), t.synthetics=List()
t=()=Lit.Unit, t.symbol.info=None, t.synthetics=List()
t=Use=Term.Name("Use"), t.symbol.info=Some(_empty_/Use. => final object Use extends AnyRef { +1 decls }), t.synthetics=List()
t=someMethod=Term.Name("someMethod"), t.symbol.info=Some(_empty_/Use.someMethod(). => method someMethod(i: Int): Unit), t.synthetics=List()
t=i=Term.Name("i"), t.symbol.info=Some(_empty_/Use.someMethod().(i) => param i: Int), t.synthetics=List()
t=()=Lit.Unit, t.symbol.info=None, t.synthetics=List()
t=Ob(1)=Term.Apply(Term.Name("Ob"), List(Lit.Int(1))), t.symbol.info=Some(_empty_/Ob. => final object Ob extends AnyRef { +2 decls }), t.synthetics=List()
t=Ob=Term.Name("Ob"), t.symbol.info=Some(_empty_/Ob. => final object Ob extends AnyRef { +2 decls }), t.synthetics=List(Some(Some(_empty_/Ob.apply(). => method apply(i: Int): Unit)))
t=1=Lit.Int(1), t.symbol.info=None, t.synthetics=List()
t=someMethod(1)=Term.Apply(Term.Name("someMethod"), List(Lit.Int(1))), t.symbol.info=Some(_empty_/Use.someMethod(). => method someMethod(i: Int): Unit), t.synthetics=List()
t=someMethod=Term.Name("someMethod"), t.symbol.info=Some(_empty_/Use.someMethod(). => method someMethod(i: Int): Unit), t.synthetics=List()
t=1=Lit.Int(1), t.symbol.info=None, t.synthetics=List()

Notice the line

t=Ob=Term.Name("Ob"), ..., t.synthetics=List(Some(Some(_empty_/Ob.apply(). => method apply(i: Int): Unit)))

See:

https://scalameta.org/docs/semanticdb/specification.html#synthetic

https://scalameta.org/docs/semanticdb/specification.html#synthetic-1

https://scalacenter.github.io/scalafix/docs/developers/semantic-tree.html#look-up-inferred-type-parameter

So

class MyRule extends SemanticRule("MyRule") {
  override def fix(implicit doc: SemanticDocument): Patch = {
    doc.tree
      .collect {
        case Term.Apply(fun, args) =>
          println(fun.symbol.info + ", " + fun.synthetics.map(_.symbol.map(_.info)))
          Patch.empty
      }.asPatch
  }
}

will print

Some(_empty_/Ob. => final object Ob extends AnyRef { +2 decls }), List(Some(Some(_empty_/Ob.apply(). => method apply(i: Int): Unit)))
Some(_empty_/Use.someMethod(). => method someMethod(i: Int): Unit), List()
Sign up to request clarification or add additional context in comments.

1 Comment

Ah so the scalac option was what I was missing (was wondering why synthetics are all empty...). Cheers

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.