/* 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(
          isFirstParty(),
          callee(functionDecl(
              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.";

    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);
}
