/**
 * @name Non-final method invocation in constructor
 * @description If a constructor calls a method that is overridden in a subclass, the result can be
 *              unpredictable.
 * @kind problem
 * @problem.severity error
 * @precision very-high
 * @id java/non-final-call-in-constructor
 * @tags reliability
 *       correctness
 *       logic
 */

import java

private predicate writtenInOneCallable(Field f) { strictcount(Callable m | m.writes(f)) = 1 }

private FieldWrite fieldWriteOnlyIn(Callable m, Field f) {
  result.getField() = f and
  m.writes(f) and
  writtenInOneCallable(f)
}

private FieldRead nonFinalFieldRead(Callable m, Field f) {
  result.getField() = f and
  result.getEnclosingCallable() = m and
  not f.isFinal()
}

private MethodAccess unqualifiedCallToNonAbstractMethod(Constructor c, Method m) {
  result.getEnclosingCallable() = c and
  (
    not exists(result.getQualifier()) or
    result.getQualifier().(ThisAccess).getType() = c.getDeclaringType()
  ) and
  m = result.getMethod() and
  not m.isAbstract()
}

from
  Constructor c, MethodAccess ma, Method m, Method n, Field f, FieldRead fa, Constructor d,
  FieldWrite fw
where
  // Method access in a constructor
  // which is an access to the object being initialized, ...
  ma = unqualifiedCallToNonAbstractMethod(c, m) and
  // ... there exists an overriding method in a subtype,
  n.overrides(m) and
  n.getDeclaringType().getASupertype+() = c.getDeclaringType() and
  // ... the method is in a supertype of c,
  m.getDeclaringType() = c.getDeclaringType().getASupertype*() and
  // ... `n` reads a non-final field `f`,
  fa = nonFinalFieldRead(n, f) and
  // ... which is declared in a subtype of `c`,
  f.getDeclaringType().getASupertype+() = c.getDeclaringType() and
  // ... `f` is written only in the subtype constructor, and
  fw = fieldWriteOnlyIn(d, f) and
  // ... the subtype constructor calls (possibly indirectly) the offending super constructor.
  d.callsConstructor+(c)
select ma, "One $@ $@ a $@ that is only $@ in the $@, so it is uninitialized in this $@.", n,
  "overriding implementation", fa, "reads", f, "subclass field", fw, "initialized", d,
  "subclass constructor", c, "super constructor"
