Scala 编译器插件,查找注释
Scala compiler-plugin, finding an annotation
我希望此插件能够获取注释 (@Typestate(filename)) 的内容。
但目前即使我打印出整棵树,我也找不到任何地方的注释。
如何从源代码中获取注释,或者在哪里可以找到有关如何执行此操作的良好文档?
我从这个 Scala 插件中提取了大部分代码 tutorial。
带有注释的文件:
class Typestate(filename:String) extends scala.annotation.StaticAnnotation
@Typestate(filename = "MyProtocol.txt")
class Cat{
def comeAlive(): Unit = println("The cat is alive")
}
object Main extends App {
val cat = new Cat()
cat.comeAlive()
}
插件代码:
package compilerPlugin
import scala.tools.nsc
import nsc.Global
import nsc.Phase
import nsc.plugins.Plugin
import nsc.plugins.PluginComponent
import scala.collection.mutable.ListBuffer
class GetFileFromAnnotation(val global: Global) extends Plugin {
import global._
val name = "GetFileFromAnnotation"
val description = "gets file from typestate annotation"
val components: List[PluginComponent] = List[PluginComponent](Component)
private object Component extends PluginComponent {
val global: GetFileFromAnnotation.this.global.type = GetFileFromAnnotation.this.global
val runsAfter: List[String] = List[String]("refchecks")
val phaseName: String = GetFileFromAnnotation.this.name
def newPhase(_prev: Phase) = new GetFileFromAnnotationPhase(_prev)
class GetFileFromAnnotationPhase(prev: Phase) extends StdPhase(prev) {
override def name: String = GetFileFromAnnotation.this.name
def apply(unit: CompilationUnit): Unit = {
printRaw(unit.body)
for(select @ Select(statements, expr) <- unit.body){
//global.reporter.error(tree.pos, "file name is here")
}
}
}
}
}
整棵树:
PackageDef(Ident(<empty>), List(ClassDef(Modifiers(), Typestate, List(), Template(List(TypeTree(), TypeTree().setOriginal(Select(Select(Ident(scala), scala.annotation), scala.annotatio
n.StaticAnnotation))), noSelfType, List(ValDef(Modifiers(PRIVATE | LOCAL | PARAMACCESSOR), TermName("filename"), TypeTree().setOriginal(Select(Select(Ident(scala), scala.Predef), TypeN
ame("String"))), EmptyTree), DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List(ValDef(Modifiers(PARAM | PARAMACCESSOR), TermName("filename"), TypeTree().setOriginal(Select(S
elect(Ident(scala), scala.Predef), TypeName("String"))), EmptyTree))), TypeTree(), Block(List(Apply(Select(Super(This(TypeName("Typestate")), typeNames.EMPTY), termNames.CONSTRUCTOR),
List())), Literal(Constant(()))))))), ClassDef(Modifiers(), Cat, List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRU
CTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(TypeName("Cat")), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(Constant(())))), DefDef(Modifie
rs(), TermName("comeAlive"), List(), List(List()), TypeTree().setOriginal(Select(Ident(scala), scala.Unit)), Apply(Select(Select(Ident(scala), scala.Predef), TermName("println")), List
(Literal(Constant("The cat is alive")))))))), ModuleDef(Modifiers(), Main, Template(List(TypeTree(), TypeTree().setOriginal(Select(Ident(scala), scala.App))), noSelfType, List(DefDef(M
odifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(TypeName("Main")), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(C
onstant(())))), ValDef(Modifiers(PRIVATE | LOCAL), TermName("cat "), TypeTree(), Apply(Select(New(Ident(Cat)), termNames.CONSTRUCTOR), List())), DefDef(Modifiers(METHOD | STABLE | ACCE
SSOR), TermName("cat"), List(), List(), TypeTree(), Select(This(TypeName("Main")), TermName("cat "))), Apply(Select(Select(This(TypeName("Main")), TermName("cat")), TermName("comeAlive
")), List()))))))
Phase好像不对。如果我将 refchecks
更改为 parser
,那么具有以下组件的插件
private object Component extends PluginComponent {
val global: GetFileFromAnnotation.this.global.type = GetFileFromAnnotation.this.global
val runsAfter: List[String] = List[String]("parser")
val phaseName: String = GetFileFromAnnotation.this.name
def newPhase(_prev: Phase) = new GetFileFromAnnotationPhase(_prev)
class GetFileFromAnnotationPhase(prev: Phase) extends StdPhase(prev) {
override def name: String = GetFileFromAnnotation.this.name
def apply(unit: CompilationUnit): Unit = {
for(tree@q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" <- unit.body){
global.reporter.echo(tree.pos, s"tree=$tree, showRaw(tree)=${showRaw(tree)}, mods.annotations=${mods.annotations}")
}
}
}
}
产生输出
[info] .../core/src/main/scala/Main.scala:5:7: tree=@new Typestate(filename = "MyProtocol.txt") class Cat extends scala.AnyRef {
[info] def <init>() = {
[info] super.<init>();
[info] ()
[info] };
[info] def comeAlive(): Unit = println("The cat is alive")
[info] }, showRaw(tree)=ClassDef(Modifiers(NoFlags, typeNames.EMPTY, List(Apply(Select(New(Ident(TypeName("Typestate"))), termNames.CONSTRUCTOR), List(NamedArg(Ident(TermName("filename")), Literal(Constant("MyProtocol.txt"))))))), TypeName("Cat"), List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))), DefDef(Modifiers(), TermName("comeAlive"), List(), List(List()), Ident(TypeName("Unit")), Apply(Ident(TermName("println")), List(Literal(Constant("The cat is alive")))))))), mods.annotations=List(new Typestate(filename = "MyProtocol.txt"))
[info] class Cat{
[info] ^
正确 mods.annotations=List(new Typestate(filename = "MyProtocol.txt"))
.
但是如果我甚至将阶段更改为 namer
(parser
之后的下一个阶段),那么插件会生成
[info] .../core/src/main/scala/Main.scala:5:7: tree=@Typestate("MyProtocol.txt") class Cat extends scala.AnyRef {
[info] def <init>(): Cat = {
[info] Cat.super.<init>();
[info] ()
[info] };
[info] def comeAlive(): Unit = scala.Predef.println("The cat is alive")
[info] }, showRaw(tree)=ClassDef(Modifiers(), Cat, List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(TypeName("Cat")), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(Constant(())))), DefDef(Modifiers(), TermName("comeAlive"), List(), List(List()), TypeTree().setOriginal(Select(Ident(scala), scala.Unit)), Apply(Select(Select(Ident(scala), scala.Predef), TermName("println")), List(Literal(Constant("The cat is alive")))))))), mods.annotations=List()
[info] class Cat{
[info] ^
已经空 mods.annotations=List()
.
在 namer
阶段之后,您不应该在树中而是在其符号中查找注释。
的确如此,
global.reporter.echo(tree.pos, tree.symbol.annotations.mkString(", "))
产生
[info] .../core/src/main/scala/Main.scala:5:7: Typestate("MyProtocol.txt")
[info] class Cat{
[info] ^
学习资源:
Seth Tisue - Scala 编译器插件 101
视频https://www.youtube.com/watch?v=h5NZjuxS5Qo
幻灯片https://docs.google.com/presentation/d/1KtJMd27yGWmr7E2yxKC_ipMeaP_vr1-6wVJ-QcqS_Vc/edit?usp=sharing
代码https://github.com/SethTisue/cloc-plugin
多蒂的变化:http://dotty.epfl.ch/docs/reference/changed-features/compiler-plugins.html
我希望此插件能够获取注释 (@Typestate(filename)) 的内容。 但目前即使我打印出整棵树,我也找不到任何地方的注释。
如何从源代码中获取注释,或者在哪里可以找到有关如何执行此操作的良好文档?
我从这个 Scala 插件中提取了大部分代码 tutorial。
带有注释的文件:
class Typestate(filename:String) extends scala.annotation.StaticAnnotation
@Typestate(filename = "MyProtocol.txt")
class Cat{
def comeAlive(): Unit = println("The cat is alive")
}
object Main extends App {
val cat = new Cat()
cat.comeAlive()
}
插件代码:
package compilerPlugin
import scala.tools.nsc
import nsc.Global
import nsc.Phase
import nsc.plugins.Plugin
import nsc.plugins.PluginComponent
import scala.collection.mutable.ListBuffer
class GetFileFromAnnotation(val global: Global) extends Plugin {
import global._
val name = "GetFileFromAnnotation"
val description = "gets file from typestate annotation"
val components: List[PluginComponent] = List[PluginComponent](Component)
private object Component extends PluginComponent {
val global: GetFileFromAnnotation.this.global.type = GetFileFromAnnotation.this.global
val runsAfter: List[String] = List[String]("refchecks")
val phaseName: String = GetFileFromAnnotation.this.name
def newPhase(_prev: Phase) = new GetFileFromAnnotationPhase(_prev)
class GetFileFromAnnotationPhase(prev: Phase) extends StdPhase(prev) {
override def name: String = GetFileFromAnnotation.this.name
def apply(unit: CompilationUnit): Unit = {
printRaw(unit.body)
for(select @ Select(statements, expr) <- unit.body){
//global.reporter.error(tree.pos, "file name is here")
}
}
}
}
}
整棵树:
PackageDef(Ident(<empty>), List(ClassDef(Modifiers(), Typestate, List(), Template(List(TypeTree(), TypeTree().setOriginal(Select(Select(Ident(scala), scala.annotation), scala.annotatio
n.StaticAnnotation))), noSelfType, List(ValDef(Modifiers(PRIVATE | LOCAL | PARAMACCESSOR), TermName("filename"), TypeTree().setOriginal(Select(Select(Ident(scala), scala.Predef), TypeN
ame("String"))), EmptyTree), DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List(ValDef(Modifiers(PARAM | PARAMACCESSOR), TermName("filename"), TypeTree().setOriginal(Select(S
elect(Ident(scala), scala.Predef), TypeName("String"))), EmptyTree))), TypeTree(), Block(List(Apply(Select(Super(This(TypeName("Typestate")), typeNames.EMPTY), termNames.CONSTRUCTOR),
List())), Literal(Constant(()))))))), ClassDef(Modifiers(), Cat, List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRU
CTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(TypeName("Cat")), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(Constant(())))), DefDef(Modifie
rs(), TermName("comeAlive"), List(), List(List()), TypeTree().setOriginal(Select(Ident(scala), scala.Unit)), Apply(Select(Select(Ident(scala), scala.Predef), TermName("println")), List
(Literal(Constant("The cat is alive")))))))), ModuleDef(Modifiers(), Main, Template(List(TypeTree(), TypeTree().setOriginal(Select(Ident(scala), scala.App))), noSelfType, List(DefDef(M
odifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(TypeName("Main")), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(C
onstant(())))), ValDef(Modifiers(PRIVATE | LOCAL), TermName("cat "), TypeTree(), Apply(Select(New(Ident(Cat)), termNames.CONSTRUCTOR), List())), DefDef(Modifiers(METHOD | STABLE | ACCE
SSOR), TermName("cat"), List(), List(), TypeTree(), Select(This(TypeName("Main")), TermName("cat "))), Apply(Select(Select(This(TypeName("Main")), TermName("cat")), TermName("comeAlive
")), List()))))))
Phase好像不对。如果我将 refchecks
更改为 parser
,那么具有以下组件的插件
private object Component extends PluginComponent {
val global: GetFileFromAnnotation.this.global.type = GetFileFromAnnotation.this.global
val runsAfter: List[String] = List[String]("parser")
val phaseName: String = GetFileFromAnnotation.this.name
def newPhase(_prev: Phase) = new GetFileFromAnnotationPhase(_prev)
class GetFileFromAnnotationPhase(prev: Phase) extends StdPhase(prev) {
override def name: String = GetFileFromAnnotation.this.name
def apply(unit: CompilationUnit): Unit = {
for(tree@q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" <- unit.body){
global.reporter.echo(tree.pos, s"tree=$tree, showRaw(tree)=${showRaw(tree)}, mods.annotations=${mods.annotations}")
}
}
}
}
产生输出
[info] .../core/src/main/scala/Main.scala:5:7: tree=@new Typestate(filename = "MyProtocol.txt") class Cat extends scala.AnyRef {
[info] def <init>() = {
[info] super.<init>();
[info] ()
[info] };
[info] def comeAlive(): Unit = println("The cat is alive")
[info] }, showRaw(tree)=ClassDef(Modifiers(NoFlags, typeNames.EMPTY, List(Apply(Select(New(Ident(TypeName("Typestate"))), termNames.CONSTRUCTOR), List(NamedArg(Ident(TermName("filename")), Literal(Constant("MyProtocol.txt"))))))), TypeName("Cat"), List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))), DefDef(Modifiers(), TermName("comeAlive"), List(), List(List()), Ident(TypeName("Unit")), Apply(Ident(TermName("println")), List(Literal(Constant("The cat is alive")))))))), mods.annotations=List(new Typestate(filename = "MyProtocol.txt"))
[info] class Cat{
[info] ^
正确 mods.annotations=List(new Typestate(filename = "MyProtocol.txt"))
.
但是如果我甚至将阶段更改为 namer
(parser
之后的下一个阶段),那么插件会生成
[info] .../core/src/main/scala/Main.scala:5:7: tree=@Typestate("MyProtocol.txt") class Cat extends scala.AnyRef {
[info] def <init>(): Cat = {
[info] Cat.super.<init>();
[info] ()
[info] };
[info] def comeAlive(): Unit = scala.Predef.println("The cat is alive")
[info] }, showRaw(tree)=ClassDef(Modifiers(), Cat, List(), Template(List(Select(Ident(scala), TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(TypeName("Cat")), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(Constant(())))), DefDef(Modifiers(), TermName("comeAlive"), List(), List(List()), TypeTree().setOriginal(Select(Ident(scala), scala.Unit)), Apply(Select(Select(Ident(scala), scala.Predef), TermName("println")), List(Literal(Constant("The cat is alive")))))))), mods.annotations=List()
[info] class Cat{
[info] ^
已经空 mods.annotations=List()
.
在 namer
阶段之后,您不应该在树中而是在其符号中查找注释。
的确如此,
global.reporter.echo(tree.pos, tree.symbol.annotations.mkString(", "))
产生
[info] .../core/src/main/scala/Main.scala:5:7: Typestate("MyProtocol.txt")
[info] class Cat{
[info] ^
学习资源:
Seth Tisue - Scala 编译器插件 101
视频https://www.youtube.com/watch?v=h5NZjuxS5Qo
幻灯片https://docs.google.com/presentation/d/1KtJMd27yGWmr7E2yxKC_ipMeaP_vr1-6wVJ-QcqS_Vc/edit?usp=sharing
代码https://github.com/SethTisue/cloc-plugin
多蒂的变化:http://dotty.epfl.ch/docs/reference/changed-features/compiler-plugins.html