/**
 * @name Spin on field
 * @description Repeatedly reading a non-volatile field within the condition of an empty loop may
 *              result in an infinite loop.
 * @kind problem
 * @problem.severity warning
 * @precision medium
 * @id java/spin-on-field
 * @tags efficiency
 *       correctness
 *       concurrency
 */

import java

/** A numerical comparison or an equality test. */
class ComparisonOrEqTestExpr extends Expr {
  ComparisonOrEqTestExpr() {
    this instanceof ComparisonExpr or
    this instanceof EqualityTest
  }
}

/** An empty statement or block. */
class Empty extends Stmt {
  Empty() {
    this instanceof EmptyStmt or
    this.(Block).getNumStmt() = 0
  }
}

/** An empty loop statement. */
class EmptyLoop extends Stmt {
  EmptyLoop() {
    exists(ForStmt stmt | stmt = this |
      count(stmt.getAnInit()) = 0 and
      count(stmt.getAnUpdate()) = 0 and
      stmt.getStmt() instanceof Empty
    )
    or
    this.(WhileStmt).getStmt() instanceof Empty
    or
    this.(DoStmt).getStmt() instanceof Empty
  }

  Expr getCondition() {
    result = this.(ForStmt).getCondition() or
    result = this.(WhileStmt).getCondition() or
    result = this.(DoStmt).getCondition()
  }
}

/** An access to a field in this object. */
class FieldAccessInThis extends VarAccess {
  FieldAccessInThis() {
    this.getVariable() instanceof Field and
    (not this.hasQualifier() or this.getQualifier() instanceof ThisAccess)
  }
}

from EmptyLoop loop, FieldAccessInThis access, Field field, ComparisonOrEqTestExpr expr
where
  loop.getCondition() = expr and
  access.getParent() = expr and
  field = access.getVariable() and
  field.isStatic() and
  not field.isFinal() and
  not field.isVolatile() and
  field.getType() instanceof RefType
select access,
  "Spinning on " + field.getName() + " in " + loop.getEnclosingCallable().getName() + "."
