如何在编译器插件中找到Scala程序中的语句?

我正在编写一个 Scala编译器插件,并且已经开始编写“apply(unit:compilationunit方法).但是该方法中的语法超出了我的范围.以下代码来自 http://www.scala-lang.org/node/140

for ( tree @ Apply(Select(rcvr,nme.DIV),List(Literal(Constant(0)))) <- unit.body;
         if rcvr.tpe <:< deFinitions.IntClass.tpe) {
        unit.error(tree.pos,"definitely division by zero")
      }

该表达式将所有除以零.我无法弄清楚如何做类似的事情来找到所有可执行语句(TermTrees ??).

解决方法

好的,假设下面的文件是TwoStatements.scala:

class TwoStatements {
  def f {
    println("first statement")
    println("second statement")
  }
}

试试这些命令:

scalac -Yshow-trees -Xprint:typer TwoStatements.scala 
scalac -Yshow-trees-compact -Xprint:typer TwoStatements.scala 
scalac -Yshow-trees-stringified -Xprint:typer TwoStatements.scala 
scalac -Ybrowse:typer TwoStatements.scala

请注意,typer是产生输出的阶段.因此,请选择适合您自己的插件运行阶段的阶段.

-Yshow-trees-compact是一个产生与你自己的代码中使用的输出完全(或非常接近)的输出,但它很难阅读.

其他的更容易阅读,但可能会混淆翻译成您自己的代码. -Yshow-trees-stringified可能比-Yshow-trees更有用,因为它显示的信息最多.另一方面,-Ybrowse:typer是交互式的,并显示所选树节点的代码,这可能会有所帮助,特别是如果你查看较大的程序.

如果您尝试使用链接博客中的示例-Yshow-trees-compact,您将看到以下代码段:

Apply(
   Select(
     Select(This(newTypeName("Test")),newTermName("five")),// assigned to rcvr
     newTermName("$div")                                     // compared to nme.DIV
   ),List(Literal(Constant(0))))                               // as is
 )

所以我建议你看看你想要处理的代码是如何在你的插件工作的阶段被翻译成的,然后只需抓住代码片段,并用变量替换任何不感兴趣的部分.

您会注意到每个DefDef都将body作为其第六个元素.它可能是一个带有多个语句列表的块,它可能是方法调用(Apply),getter(Select),assigment(Assign),if语句(If)等等.其他类型的声明,例如ValDef,也有与之关联的代码.

如果您正在寻找像tree @ Statement(…)这样的东西,它就不存在了.您可以使用TermTree(或者,更好的方法是isTerm)来识别代表代码内容,但这不会让您将表达式或完整块的部分语句分开.

查看实际代码的AST,并了解它是如何工作的.

编辑

观看a presentation只是在scala / reflecti / api / Trees.scala文件的末尾引用了我的评论

// A standard pattern match
  case EmptyTree =>
  case PackageDef(pid,stats) =>
     // package pid { stats }
  case ClassDef(mods,name,tparams,impl) =>
     // mods class name [tparams] impl   where impl = extends parents { defs }
  case ModuleDef(mods,impl) =>                             (eliminated by refcheck)
     // mods object name impl  where impl = extends parents { defs }
  case ValDef(mods,tpt,rhs) =>
     // mods val name: tpt = rhs
     // note missing type information is expressed by tpt = TypeTree()
  case DefDef(mods,vparamss,rhs) =>
     // mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs
     // note missing type information is expressed by tpt = TypeTree()
  case TypeDef(mods,rhs) =>                       (eliminated by erasure)
     // mods type name[tparams] = rhs
     // mods type name[tparams] >: lo <: hi,where lo,hi are in a TypeBoundsTree,and DEFERRED is set in mods
  case LabelDef(name,params,rhs) =>
     // used for tailcalls and like
     // while/do are desugared to label defs as follows:
     // while (cond) body ==> LabelDef($L,List(),if (cond) { body; L$() } else ())
     // do body while (cond) ==> LabelDef($L,body; if (cond) L$() else ())
  case Import(expr,selectors) =>                                 (eliminated by typecheck)
     // import expr.{selectors}
     // Selectors are a list of pairs of names (from,to).
     // The last (and maybe only name) may be a nme.WILDCARD
     // for instance
     //   import qual.{x,y => z,_}  would be represented as
     //   Import(qual,List(("x","x"),("y","z"),(WILDCARD,null)))
  case Template(parents,self,body) =>
     // extends parents { self => body }
     // if self is missing it is represented as emptyValDef
  case Block(stats,expr) =>
     // { stats; expr }
  case CaseDef(pat,guard,body) =>                               (eliminated by transmatch/explicitouter)
    // case pat if guard => body
  case Alternative(trees) =>                                      (eliminated by transmatch/explicitouter)
    // pat1 | ... | patn
  case Star(elem) =>                                              (eliminated by transmatch/explicitouter)
    // pat*
  case Bind(name,body) =>                                        (eliminated by transmatch/explicitouter)
    // name @ pat
  case UnApply(fun: Tree,args)                                   (introduced by typer,eliminated by transmatch/explicitouter)
    // used for unapply's
  case ArrayValue(elemtpt,trees) =>                              (introduced by uncurry)
    // used to pass arguments to vararg arguments
    // for instance,printf("%s%d",foo,42) is translated to after uncurry to:
    // Apply(
    //   Ident("printf"),//   Literal("%s%d"),//   ArrayValue(<Any>,List(Ident("foo"),Literal(42))))
  case Function(vparams,body) =>                                 (eliminated by lambdalift)
    // vparams => body  where vparams:List[ValDef]
  case Assign(lhs,rhs) =>
    // lhs = rhs
  case AssignorNamedArg(lhs,rhs) =>                              (eliminated by typer,resurrected by reifier)
    // @annotation(lhs = rhs)
  case If(cond,thenp,elsep) =>
    // if (cond) thenp else elsep
  case Match(selector,cases) =>
    // selector match { cases }
  case Return(expr) =>
    // return expr
  case Try(block,catches,finalizer) =>
    // try block catch { catches } finally finalizer where catches: List[CaseDef]
  case Throw(expr) =>
    // throw expr
  case New(tpt) =>
    // new tpt   always in the context: (new tpt).<init>[targs](args)
  case Typed(expr,tpt) =>                                        (eliminated by erasure)
    // expr: tpt
  case TypeApply(fun,args) =>
    // fun[args]
  case Apply(fun,args) =>
    // fun(args)
    // for instance fun[targs](args)  is expressed as  Apply(TypeApply(fun,targs),args)
  case ApplyDynamic(qual,args)                                   (introduced by erasure,eliminated by cleanup)
    // fun(args)
  case Super(qual,mix) =>
    // qual.super[mix]     qual is always This(something),if mix is empty,it is tpnme.EMPTY
  case This(qual) =>
    // qual.this
  case Select(qualifier,selector) =>
    // qualifier.selector
  case Ident(name) =>
    // name
    // note: type checker converts idents that refer to enclosing fields or methods
    // to selects; name ==> this.name
  case ReferencetoBoxed(ident) =>                                 (created by typer,eliminated by lambdalift)
    // synthetic node emitted by macros to reference capture vars directly without going through ``elem''
    // var x = ...; fun { x } will emit Ident(x),which gets transformed to Select(Ident(x),"elem")
    // if ReferencetoBoxed were used instead of Ident,no transformation would be performed
  case Literal(value) =>
    // value
  case TypeTree() =>                                              (introduced by refcheck)
    // a type that's not written out,but given in the tpe attribute
  case Annotated(annot,arg) =>                                   (eliminated by typer)
    // arg @annot  for types,arg: @annot for exprs
  case SingletonTypeTree(ref) =>                                  (eliminated by uncurry)
    // ref.type
  case SelectFromTypeTree(qualifier,selector) =>                 (eliminated by uncurry)
    // qualifier # selector,a path-dependent type p.T is expressed as p.type # T
  case CompoundTypeTree(templ: Template) =>                       (eliminated by uncurry)
    // parent1 with ... with parentN { refinement }
  case AppliedTypeTree(tpt,args) =>                              (eliminated by uncurry)
    // tpt[args]
  case TypeBoundsTree(lo,hi) =>                                  (eliminated by uncurry)
    // >: lo <: hi
  case ExistentialTypeTree(tpt,whereClauses) =>                  (eliminated by uncurry)
    // tpt forSome { whereClauses }

相关文章

共收录Twitter的14款开源软件,第1页Twitter的Emoji表情 Tw...
Java和Scala中关于==的区别Java:==比较两个变量本身的值,即...
本篇内容主要讲解“Scala怎么使用”,感兴趣的朋友不妨来看看...
这篇文章主要介绍“Scala是一种什么语言”,在日常操作中,相...
这篇文章主要介绍“Scala Trait怎么使用”,在日常操作中,相...
这篇文章主要介绍“Scala类型检查与模式匹配怎么使用”,在日...