/**
 * Provides predicates for finding variable references and declarations
 * in a given function or toplevel.
 */

import javascript

/**
 * Classification of variable references; all references have kind `Ref`,
 * but only declarations have kind `Decl`.
 *
 * Note that references that are not declarations are called accesses elsewhere,
 * but they are not treated specially in this context.
 */
newtype RefKind = Ref() or Decl()

/**
 * Gets a reference to `var` (if `kind` is `Ref()`) or declaration of
 * `var` (if `kind` is `Decl()`) in `sc`.
 */
VarRef refInContainer(Variable var, RefKind kind, StmtContainer sc) {
  result.getVariable() = var and
  result.getContainer() = sc and
  (kind = Decl() implies result instanceof VarDecl)
}

/**
 * Gets the textually first reference to `var` (if `kind` is `Ref()`) or
 * declaration of `var` (if `kind` is `Decl()`) in `sc`.
 */
VarRef firstRefInContainer(Variable var, RefKind kind, StmtContainer sc) {
  result = min(refInContainer(var, kind, sc) as ref
               order by ref.getLocation().getStartLine(),
                        ref.getLocation().getStartColumn())
}

/**
 * Gets a reference to `var` (if `kind` is `Ref()`) or declaration of
 * `var` (if `kind` is `Decl()`) in `tl`.
 */
VarRef refInTopLevel(Variable var, RefKind kind, TopLevel tl) {
  result.getVariable() = var and
  result.getTopLevel() = tl and
  (kind = Decl() implies result instanceof VarDecl)
}

/**
 * Gets the textually first reference to `var` (if `kind` is `Ref()`) or
 * declaration of `var` (if `kind` is `Decl()`) in `tl`.
 */
VarRef firstRefInTopLevel(Variable var, RefKind kind, TopLevel tl) {
  result = min(refInTopLevel(var, kind, tl) as ref
               order by ref.getLocation().getStartLine(),
                        ref.getLocation().getStartColumn())
}
