void DefaultArgumentsCheck::check(const MatchFinder::MatchResult &Result) {
  if (const auto *S =
          Result.Nodes.getNodeAs<CXXDefaultArgExpr>("stmt")) {
    diag(S->getUsedLocation(),
         "calling a function that uses a default argument is disallowed");
    diag(S->getParam()->getBeginLoc(), "default parameter was declared here",
         DiagnosticIDs::Note);
  } else if (const ParmVarDecl *D =
          Result.Nodes.getNodeAs<ParmVarDecl>("decl")) {
    SourceRange DefaultArgRange = D->getDefaultArgRange();

    if (DefaultArgRange.getEnd() != D->getEndLoc()) {
      return;
    } else if (DefaultArgRange.getBegin().isMacroID()) {
      diag(D->getBeginLoc(),
           "declaring a parameter with a default argument is disallowed");
    } else {
      SourceLocation StartLocation =
          D->getName().empty() ? D->getBeginLoc() : D->getLocation();

      SourceRange RemovalRange(Lexer::getLocForEndOfToken(
             StartLocation, 0,
             *Result.SourceManager,
             Result.Context->getLangOpts()
           ),
           DefaultArgRange.getEnd()
         );

      diag(D->getBeginLoc(),
           "declaring a parameter with a default argument is disallowed")
          << D << FixItHint::CreateRemoval(RemovalRange);
    }
  }
}
void UnusedParametersCheck::warnOnUnusedParameter(
    const MatchFinder::MatchResult &Result, const FunctionDecl *Function,
    unsigned ParamIndex) {
  const auto *Param = Function->getParamDecl(ParamIndex);
  auto MyDiag = diag(Param->getLocation(), "parameter '%0' is unused")
                << Param->getName();

  auto UsedByRef = [&] {
    return !ast_matchers::match(
                decl(hasDescendant(
                    declRefExpr(to(equalsNode(Function)),
                                unless(hasAncestor(
                                    callExpr(callee(equalsNode(Function)))))))),
                *Result.Context->getTranslationUnitDecl(), *Result.Context)
                .empty();
  };

  // Comment out parameter name for non-local functions.
  if (Function->isExternallyVisible() ||
      !Result.SourceManager->isInMainFile(Function->getLocation()) ||
      UsedByRef()) {
    SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd());
    // Note: We always add a space before the '/*' to not accidentally create a
    // '*/*' for pointer types, which doesn't start a comment. clang-format will
    // clean this up afterwards.
    MyDiag << FixItHint::CreateReplacement(
        RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());
    return;
  }

  // Fix all redeclarations.
  for (const FunctionDecl *FD : Function->redecls())
    if (FD->param_size())
      MyDiag << removeParameter(FD, ParamIndex);

  // Fix all call sites.
  auto CallMatches = ast_matchers::match(
      decl(forEachDescendant(
          callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))),
      *Result.Context->getTranslationUnitDecl(), *Result.Context);
  for (const auto &Match : CallMatches)
    MyDiag << removeArgument(Match.getNodeAs<CallExpr>("x"), ParamIndex);
}
void UnusedParametersCheck::warnOnUnusedParameter(
    const MatchFinder::MatchResult &Result, const FunctionDecl *Function,
    unsigned ParamIndex) {
  const auto *Param = Function->getParamDecl(ParamIndex);
  auto MyDiag = diag(Param->getLocation(), "parameter '%0' is unused")
                << Param->getName();

  auto UsedByRef = [&] {
    return !ast_matchers::match(
                decl(hasDescendant(
                    declRefExpr(to(equalsNode(Function)),
                                unless(hasAncestor(
                                    callExpr(callee(equalsNode(Function)))))))),
                *Result.Context->getTranslationUnitDecl(), *Result.Context)
                .empty();
  };

  // Comment out parameter name for non-local functions.
  if ((Function->isExternallyVisible() &&
       Function->getStorageClass() != StorageClass::SC_Static) ||
      UsedByRef()) {
    SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd());
    MyDiag << FixItHint::CreateReplacement(
        RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());
    return;
  }

  // Fix all redeclarations.
  for (const FunctionDecl *FD : Function->redecls())
    MyDiag << removeParameter(FD, ParamIndex);

  // Fix all call sites.
  auto CallMatches = ast_matchers::match(
      decl(forEachDescendant(
          callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))),
      *Result.Context->getTranslationUnitDecl(), *Result.Context);
  for (const auto &Match : CallMatches)
    MyDiag << removeArgument(Match.getNodeAs<CallExpr>("x"), ParamIndex);
}