If I have the following type
Defn.Var(mods, pats, decltpe, rhs)
in scalameta it might happen that decltype is set to None
for a variable like this:
var x = 10
I still want to know the exact type of the variable x
which Scala has inferred without checking the type of the assignment expression myself.
I know that I can just get the information that 10 is an Int literal but for more complex expressions it might be helpful to have some helper function for the type.
Is there any function in scalameta which gives you the inferred type?
libraryDependencies += "org.scalameta" %% "scalameta" % "4.2.0"
mostly works with source code (parses it to trees, transforms trees) before its compilation i.e. when there can't be any information about symbols and types. To get information about symbols and types one should start compiler (one of).
This is exactly what SemanticDB is for. If you switch on semanticdb-scalac
compiler plugin
addCompilerPlugin("org.scalameta" % "semanticdb-scalac" % "4.1.0" cross CrossVersion.full)
scalacOptions ++= Seq(
"-Yrangepos",
"-P:semanticdb:synthetics:on",
)
then upon compilation it will generate .semanticdb
files near to .class
files. Information about symbols and types will be there. These files can be parsed with semanticdb
libraryDependencies += "org.scalameta" %% "semanticdb" % "4.1.0"
For example if you have App1.scala
object App1 {
var x = 10
}
then
import java.nio.file.Paths
import scala.meta.internal.semanticdb.Locator
Locator(
Paths.get("./target/scala-2.12/classes/META-INF/semanticdb/src/main/scala/App1.scala.semanticdb")
)((path, textDocuments) =>
println(textDocuments)
)
produces
TextDocuments(Vector(TextDocument(SEMANTICDB4,src/main/scala/App1.scala,,29E9BFD566BEFD436FBE59679524E53D,SCALA,Vector(SymbolInformation(_empty_/App1.`x_=`().,SCALA,METHOD,2048,x_=,MethodSignature(Some(Scope(Vector(),Vector())),Vector(Scope(Vector(_empty_/App1.`x_=`().(x$1)),Vector())),TypeRef(Empty,scala/Unit#,Vector())),Vector(),PublicAccess()), SymbolInformation(_empty_/App1.,SCALA,OBJECT,8,App1,ClassSignature(Some(Scope(Vector(),Vector())),Vector(TypeRef(Empty,scala/AnyRef#,Vector())),Empty,Some(Scope(Vector(_empty_/App1.x()., _empty_/App1.`x_=`().),Vector()))),Vector(),PublicAccess()), SymbolInformation(_empty_/App1.`x_=`().(x$1),SCALA,PARAMETER,0,x$1,ValueSignature(TypeRef(Empty,scala/Int#,Vector())),Vector(),Empty), SymbolInformation(_empty_/App1.x().,SCALA,METHOD,2048,x,MethodSignature(Some(Scope(Vector(),Vector())),Vector(),TypeRef(Empty,scala/Int#,Vector())),Vector(),PublicAccess())),Vector(SymbolOccurrence(Some(Range(1,6,1,7)),_empty_/App1.x().,DEFINITION), SymbolOccurrence(Some(Range(0,7,0,11)),_empty_/App1.,DEFINITION)),Vector(),Vector())))
You can pretty-print this file with Metap
import scala.meta.cli.Metap
Metap.main(Array("target/scala-2.12/classes/META-INF/semanticdb/src/main/scala/App1.scala.semanticdb"))
Output
src/main/scala/App1.scala
-------------------------
Summary:
Schema => SemanticDB v4
Uri => src/main/scala/App1.scala
Text => empty
Language => Scala
Symbols => 4 entries
Occurrences => 2 entries
Symbols:
_empty_/App1. => final object App1 extends AnyRef { +2 decls }
_empty_/App1.`x_=`(). => var method x_=(x$1: Int): Unit
_empty_/App1.`x_=`().(x$1) => param x$1: Int
_empty_/App1.x(). => var method x: Int
Occurrences:
[0:7..0:11) <= _empty_/App1.
[1:6..1:7) <= _empty_/App1.x().
And
import scala.meta.internal.semanticdb.TypeRef
import scala.meta.internal.semanticdb.SignatureMessage.SealedValue.{ClassSignature, MethodSignature, TypeSignature, ValueSignature, Empty}
Locator(
Paths.get("./target/scala-2.12/classes/META-INF/semanticdb/src/main/scala/App1.scala.semanticdb")
)((path, textDocuments) =>
for {
document <- textDocuments.documents
symbol <- document.symbols
} println(s"symbol=${symbol.displayName}, ${symbol.signature.asMessage.sealedValue match {
case v: ValueSignature => s"ValueSignature, type=${v.value.tpe match { case t: TypeRef => t.symbol}}"
case m: MethodSignature => s"MethodSignature, returnType=${m.value.returnType match { case t: TypeRef => t.symbol}}"
case c: ClassSignature => "ClassSignature"
case t: TypeSignature => "TypeSignature"
case Empty => "Empty"
}}")
)
produces
symbol=x_=, MethodSignature, returnType=scala/Unit#
symbol=App1, ClassSignature
symbol=x$1, ValueSignature, type=scala/Int#
symbol=x, MethodSignature, returnType=scala/Int#
Scheme is here.
Also you can try Scalafix
sbt new scalacenter/scalafix.g8 --repo="scalafixdemo"
cd scalafix
sbt ~tests/test
If you write in input/src/main/scala/fix/Scalafixdemo.scala
package fix
object Scalafixdemo {
var x = 10
}
and in rules/src/main/scala/fix/Scalafixdemo.scala
package fix
import scalafix.v1._
import scala.meta._
class Scalafixdemo extends SemanticRule("Scalafixdemo") {
override def fix(implicit doc: SemanticDocument): Patch = {
// println("Tree.syntax: " + doc.tree.syntax)
// println("Tree.structure: " + doc.tree.structure)
// println("Tree.structureLabeled: " + doc.tree.structureLabeled)
doc.tree.traverse {
case t@q"..$mods var ..$patsnel: $tpeopt = $expropt" =>
println(t.symbol.info.get.signature)
}
Patch.empty
}
}
then it will print : Int