Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Latest commit

 

History

History
47 lines (39 loc) · 2.13 KB

scoping-rules.md

File metadata and controls

47 lines (39 loc) · 2.13 KB

Writing Scoping Rules

Rules that need to track scoping information can take advantage of the ScopingRule base trait to do so. Much like for warning rules, scoping rules simply accumulate warnings by using a nok helper method. However, they also provide a scoping helper that enables simple scope accumulation:

def enter(owner : Owner) : Unit

will add owner to the scoping stack and the state provider can be queried to check whether we are currently in a particular owner with another helper:

def in(owner : Onwer) : Boolean

To illustrate ScopingRule usage, we will implement a recursive method definition checker that searches for weird definitions that point to themselves (see the full source at StupidRecursion).

We start by defining the rule class:

import scala.tools.abide._
import scala.tools.abide.traversal._

class StupidRecursion(val context : Context) extends ScopingRule {
  val name = "stupid-recursion"
}

and we provide the required Warning type:

case class Warning(tree : Tree) extends RuleWarning {
  val pos = tree.pos
  val message = s"The value $tree is recursively used " +
                 "in it's directly defining scope"
}

To manage scoping, we want to register entry of any method definition in the step partial function. To implement this, we use the enter helper and apply it to any member def declaration discovered during AST traversal:

case defDef @ q"def $name : $tpt = $body" => enter(defDef.symbol)

Scope is automatically handled by the ScopingRule trait, and we can now query the internal traversal state with state.in(sym) to evaluate the current scoping state.

Now that we have scope, we implement stupid recursion checking by simply verifying that definition access doesn't point to the current scope (state in tree.symbol):

case id @ Ident(_) if id.symbol != null && (state in id.symbol) =>
  nok(Warning(id))
case s @ Select(_, _) if s.symbol != null && (state in s.symbol) =>
  nok(Warning(s))

And... we're done!