如果我有以下类型
Defn.Var(mods, pats, decltpe, rhs)
在 scalameta 中,可能会发生 decltype 设置为None
这样的变量的情况:
var x = 10
我仍然想知道 Scala 在没有自己检查赋值表达式类型的情况下推断出的变量x
的确切类型。我知道我可以只得到 10 是 Int 文字的信息,但对于更复杂的表达式,为该类型提供一些辅助函数可能会有所帮助。scalameta 中是否有任何函数可以为您提供推断类型?
Scalameta
libraryDependencies += "org.scalameta" %% "scalameta" % "4.2.0"
主要在编译之前使用源代码(将其解析为树,转换树(,即当没有任何关于符号和类型的信息时。要获取有关符号和类型的信息,应启动编译器(其中之一(。
这正是SemanticDB的用途。如果您打开semanticdb-scalac
编译器插件
addCompilerPlugin("org.scalameta" % "semanticdb-scalac" % "4.1.0" cross CrossVersion.full)
scalacOptions ++= Seq(
"-Yrangepos",
"-P:semanticdb:synthetics:on",
)
然后在编译时,它将生成.semanticdb
文件接近.class
文件。有关符号和类型的信息将在那里。这些文件可以使用semanticdb
libraryDependencies += "org.scalameta" %% "semanticdb" % "4.1.0"
例如,如果您有App1.scala
object App1 {
var x = 10
}
然后
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)
)
生产
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())))
您可以使用Metap
漂亮地打印此文件
import scala.meta.cli.Metap
Metap.main(Array("target/scala-2.12/classes/META-INF/semanticdb/src/main/scala/App1.scala.semanticdb"))
输出
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().
和
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"
}}")
)
生产
symbol=x_=, MethodSignature, returnType=scala/Unit#
symbol=App1, ClassSignature
symbol=x$1, ValueSignature, type=scala/Int#
symbol=x, MethodSignature, returnType=scala/Int#
方案就在这里。
你也可以试试Scalafix
sbt new scalacenter/scalafix.g8 --repo="scalafixdemo"
cd scalafix
sbt ~tests/test
如果你写input/src/main/scala/fix/Scalafixdemo.scala
package fix
object Scalafixdemo {
var x = 10
}
并在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
}
}
然后它将打印: Int
https://github.com/DmytroMitin/scalafix-codegen