/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "FopenUsageChecker.h"
#include "CustomMatchers.h"

void FopenUsageChecker::registerMatchers(MatchFinder *AstMatcher) {

  auto hasConstCharPtrParam = [](const unsigned int Position) {
    return hasParameter(
        Position, hasType(hasCanonicalType(pointsTo(asString("const char")))));
  };

  auto hasParamOfType = [](const unsigned int Position, const char *Name) {
    return hasParameter(Position, hasType(asString(Name)));
  };

  auto hasIntegerParam = [](const unsigned int Position) {
    return hasParameter(Position, hasType(isInteger()));
  };

  AstMatcher->addMatcher(
      callExpr(
          allOf(
              isFirstParty(),
              callee(functionDecl(allOf(
                  isInSystemHeader(),
                  anyOf(
                      allOf(anyOf(allOf(hasName("fopen"),
                                        hasConstCharPtrParam(0)),
                                  allOf(hasName("fopen_s"),
                                        hasParameter(
                                            0, hasType(pointsTo(pointsTo(
                                                   asString("FILE"))))),
                                        hasConstCharPtrParam(2))),
                            hasConstCharPtrParam(1)),
                      allOf(anyOf(hasName("open"),
                                  allOf(hasName("_open"), hasIntegerParam(2)),
                                  allOf(hasName("_sopen"), hasIntegerParam(3))),
                            hasConstCharPtrParam(0), hasIntegerParam(1)),
                      allOf(hasName("_sopen_s"),
                            hasParameter(0, hasType(pointsTo(isInteger()))),
                            hasConstCharPtrParam(1), hasIntegerParam(2),
                            hasIntegerParam(3), hasIntegerParam(4)),
                      allOf(hasName("OpenFile"), hasConstCharPtrParam(0),
                            hasParamOfType(1, "LPOFSTRUCT"),
                            hasIntegerParam(2)),
                      allOf(hasName("CreateFileA"), hasConstCharPtrParam(0),
                            hasIntegerParam(1), hasIntegerParam(2),
                            hasParamOfType(3, "LPSECURITY_ATTRIBUTES"),
                            hasIntegerParam(4), hasIntegerParam(5),
                            hasParamOfType(6, "HANDLE")))))),
              unless(isInWhitelistForFopenUsage())))
          .bind("funcCall"),
      this);
}

void FopenUsageChecker::check(const MatchFinder::MatchResult &Result) {
  const CallExpr *FuncCall = Result.Nodes.getNodeAs<CallExpr>("funcCall");
  static const char *ExtraInfo =
      "On Windows executed functions: fopen, fopen_s, open, _open, _sopen, "
      "_sopen_s, OpenFile, CreateFileA should never be used due to lossy "
      "conversion from UTF8 to ANSI.";

  if (FuncCall) {
    diag(FuncCall->getBeginLoc(),
         "Usage of ASCII file functions (here %0) is forbidden on Windows.",
         DiagnosticIDs::Warning)
        << FuncCall->getDirectCallee()->getName();
    diag(FuncCall->getBeginLoc(), ExtraInfo, DiagnosticIDs::Note);
  }
}