如果 decltpe 为空,如何使用 scalameta 获取变量的类型



如果我有以下类型

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

相关内容

  • 没有找到相关文章

最新更新