/**
 * @name Too few assertions
 * @description Each function over 20 logical lines should have at least two assertions.
 * @kind problem
 * @id cpp/power-of-10/assertion-density
 * @problem.severity warning
 */

import semmle.code.cpp.commons.Assertions

class MacroFunctionCall extends MacroInvocation {
  MacroFunctionCall() {
    not exists(getParentInvocation()) and
    this.getMacro().getHead().matches("%(%")
  }

  FunctionDeclarationEntry getFunction() {
    result.getFunction() = this.getAGeneratedElement().(Stmt).getEnclosingFunction()
  }
}

int logicalLength(FunctionDeclarationEntry f) {
  result = count(Stmt s | s.getEnclosingFunction() = f.getFunction() and
    s.getFile() = f.getFile() and
    not s instanceof Block and
    not s instanceof EmptyStmt and
    not exists(ForStmt for | s = for.getInitialization()) and
    not s.isAffectedByMacro()) + count(MacroFunctionCall mf | mf.getFunction() = f)
}

int assertionCount(FunctionDeclarationEntry f) {
  result = count(Assertion a | a.getAsserted().getEnclosingFunction() = f.getFunction() and a.getFile() = f.getFile())
}

from FunctionDeclarationEntry f, int numAsserts, int size, int minSize
where minSize = 20 and
  numAsserts = assertionCount(f) and
  numAsserts < 2 and
  size = logicalLength(f) and
  size > minSize
select f.getFunction(), "Function " + f.getName() + " has " + size + " logical lines, but only "
  + numAsserts + " assertion(s) -- minimum is 2 (for functions over " + minSize + " logical lines)."
