reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
//===--- CloexecCheck.cpp - clang-tidy-------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "CloexecCheck.h"
#include "../utils/ASTUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace android {

namespace {
// Helper function to form the correct string mode for Type3.
// Build the replace text. If it's string constant, add <Mode> directly in the
// end of the string. Else, add <Mode>.
std::string buildFixMsgForStringFlag(const Expr *Arg, const SourceManager &SM,
                                     const LangOptions &LangOpts, char Mode) {
  if (Arg->getBeginLoc().isMacroID())
    return (Lexer::getSourceText(
                CharSourceRange::getTokenRange(Arg->getSourceRange()), SM,
                LangOpts) +
            " \"" + Twine(Mode) + "\"")
        .str();

  StringRef SR = cast<StringLiteral>(Arg->IgnoreParenCasts())->getString();
  return ("\"" + SR + Twine(Mode) + "\"").str();
}
} // namespace

const char *CloexecCheck::FuncDeclBindingStr = "funcDecl";

const char *CloexecCheck::FuncBindingStr ="func";

void CloexecCheck::registerMatchersImpl(
    MatchFinder *Finder, internal::Matcher<FunctionDecl> Function) {
  // We assume all the checked APIs are C functions.
  Finder->addMatcher(
      callExpr(
          callee(functionDecl(isExternC(), Function).bind(FuncDeclBindingStr)))
          .bind(FuncBindingStr),
      this);
}

void CloexecCheck::insertMacroFlag(const MatchFinder::MatchResult &Result,
                                   StringRef MacroFlag, int ArgPos) {
  const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
  const auto *FlagArg = MatchedCall->getArg(ArgPos);
  const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(FuncDeclBindingStr);
  SourceManager &SM = *Result.SourceManager;

  if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
                                        Result.Context->getLangOpts(),
                                        MacroFlag))
    return;

  SourceLocation EndLoc =
      Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getEndLoc()), 0, SM,
                                 Result.Context->getLangOpts());

  diag(EndLoc, "%0 should use %1 where possible")
      << FD << MacroFlag
      << FixItHint::CreateInsertion(EndLoc, (Twine(" | ") + MacroFlag).str());
}

void CloexecCheck::replaceFunc(const MatchFinder::MatchResult &Result,
                               StringRef WarningMsg, StringRef FixMsg) {
  const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
  diag(MatchedCall->getBeginLoc(), WarningMsg)
      << FixItHint::CreateReplacement(MatchedCall->getSourceRange(), FixMsg);
}

void CloexecCheck::insertStringFlag(
    const ast_matchers::MatchFinder::MatchResult &Result, const char Mode,
    const int ArgPos) {
  const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
  const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(FuncDeclBindingStr);
  const auto *ModeArg = MatchedCall->getArg(ArgPos);

  // Check if the <Mode> may be in the mode string.
  const auto *ModeStr = dyn_cast<StringLiteral>(ModeArg->IgnoreParenCasts());
  if (!ModeStr || (ModeStr->getString().find(Mode) != StringRef::npos))
    return;

  std::string ReplacementText = buildFixMsgForStringFlag(
      ModeArg, *Result.SourceManager, Result.Context->getLangOpts(), Mode);

  diag(ModeArg->getBeginLoc(), "use %0 mode '%1' to set O_CLOEXEC")
      << FD << std::string(1, Mode)
      << FixItHint::CreateReplacement(ModeArg->getSourceRange(),
                                      ReplacementText);
}

StringRef CloexecCheck::getSpellingArg(const MatchFinder::MatchResult &Result,
                                       int N) const {
  const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
  const SourceManager &SM = *Result.SourceManager;
  return Lexer::getSourceText(
      CharSourceRange::getTokenRange(MatchedCall->getArg(N)->getSourceRange()),
      SM, Result.Context->getLangOpts());
}

} // namespace android
} // namespace tidy
} // namespace clang