//=== PolymorphicPointerChecker.cpp -----------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// PolymorphicPointerChecker issues a warning when an array of derived types
// is implicitly cast to the base type. This is bad because the size of the
// derived type may not be the same size as the base type.
//
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/AST/CharUnits.h"

using namespace clang;
using namespace ento;

namespace {
class PolymorphicPointerChecker : public Checker< check::PreStmt<CastExpr> > {
  mutable llvm::OwningPtr<BuiltinBug> BT;
public:
  void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
};
}

void PolymorphicPointerChecker::checkPreStmt(const CastExpr *CE,
          CheckerContext &C) const {
  const ImplicitCastExpr *ICE, *ICE2;
  if ((ICE = dyn_cast<ImplicitCastExpr>(CE)) == NULL)
    return;
  if (ICE->getCastKind() != CK_DerivedToBase)
    return;
  if ((ICE2 = dyn_cast<ImplicitCastExpr>(ICE->getSubExpr())) == NULL)
    return;
  if (ICE2->getCastKind() != CK_ArrayToPointerDecay)
    return;
  if (ExplodedNode *errorNode = C.generateSink()) {
    if (!BT)
      BT.reset(new BuiltinBug("Polymorphic Pointer",
                  "Using a base pointer to an array of a derived type is "
                  "unsafe because of possible size differences."));
    RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(),
                                             errorNode);
    R->addRange(CE->getSourceRange());
    C.EmitReport(R);
  }
}

void ento::registerPolymorphicPointerChecker(CheckerManager &mgr) {
  mgr.registerChecker<PolymorphicPointerChecker>();  
}

