//===--- 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 directly in the // end of the string. Else, add . 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(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 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(FuncBindingStr); const auto *FlagArg = MatchedCall->getArg(ArgPos); const auto *FD = Result.Nodes.getNodeAs(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(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(FuncBindingStr); const auto *FD = Result.Nodes.getNodeAs(FuncDeclBindingStr); const auto *ModeArg = MatchedCall->getArg(ArgPos); // Check if the may be in the mode string. const auto *ModeStr = dyn_cast(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(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