• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- CloexecCheck.cpp - clang-tidy-------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "CloexecCheck.h"
10 #include "../utils/ASTUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Lex/Lexer.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace android {
20 
21 namespace {
22 // Helper function to form the correct string mode for Type3.
23 // Build the replace text. If it's string constant, add <Mode> directly in the
24 // end of the string. Else, add <Mode>.
buildFixMsgForStringFlag(const Expr * Arg,const SourceManager & SM,const LangOptions & LangOpts,char Mode)25 std::string buildFixMsgForStringFlag(const Expr *Arg, const SourceManager &SM,
26                                      const LangOptions &LangOpts, char Mode) {
27   if (Arg->getBeginLoc().isMacroID())
28     return (Lexer::getSourceText(
29                 CharSourceRange::getTokenRange(Arg->getSourceRange()), SM,
30                 LangOpts) +
31             " \"" + Twine(Mode) + "\"")
32         .str();
33 
34   StringRef SR = cast<StringLiteral>(Arg->IgnoreParenCasts())->getString();
35   return ("\"" + SR + Twine(Mode) + "\"").str();
36 }
37 } // namespace
38 
39 const char *CloexecCheck::FuncDeclBindingStr = "funcDecl";
40 
41 const char *CloexecCheck::FuncBindingStr ="func";
42 
registerMatchersImpl(MatchFinder * Finder,internal::Matcher<FunctionDecl> Function)43 void CloexecCheck::registerMatchersImpl(
44     MatchFinder *Finder, internal::Matcher<FunctionDecl> Function) {
45   // We assume all the checked APIs are C functions.
46   Finder->addMatcher(
47       callExpr(
48           callee(functionDecl(isExternC(), Function).bind(FuncDeclBindingStr)))
49           .bind(FuncBindingStr),
50       this);
51 }
52 
insertMacroFlag(const MatchFinder::MatchResult & Result,StringRef MacroFlag,int ArgPos)53 void CloexecCheck::insertMacroFlag(const MatchFinder::MatchResult &Result,
54                                    StringRef MacroFlag, int ArgPos) {
55   const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
56   const auto *FlagArg = MatchedCall->getArg(ArgPos);
57   const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(FuncDeclBindingStr);
58   SourceManager &SM = *Result.SourceManager;
59 
60   if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
61                                         Result.Context->getLangOpts(),
62                                         MacroFlag))
63     return;
64 
65   SourceLocation EndLoc =
66       Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getEndLoc()), 0, SM,
67                                  Result.Context->getLangOpts());
68 
69   diag(EndLoc, "%0 should use %1 where possible")
70       << FD << MacroFlag
71       << FixItHint::CreateInsertion(EndLoc, (Twine(" | ") + MacroFlag).str());
72 }
73 
replaceFunc(const MatchFinder::MatchResult & Result,StringRef WarningMsg,StringRef FixMsg)74 void CloexecCheck::replaceFunc(const MatchFinder::MatchResult &Result,
75                                StringRef WarningMsg, StringRef FixMsg) {
76   const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
77   diag(MatchedCall->getBeginLoc(), WarningMsg)
78       << FixItHint::CreateReplacement(MatchedCall->getSourceRange(), FixMsg);
79 }
80 
insertStringFlag(const ast_matchers::MatchFinder::MatchResult & Result,const char Mode,const int ArgPos)81 void CloexecCheck::insertStringFlag(
82     const ast_matchers::MatchFinder::MatchResult &Result, const char Mode,
83     const int ArgPos) {
84   const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
85   const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(FuncDeclBindingStr);
86   const auto *ModeArg = MatchedCall->getArg(ArgPos);
87 
88   // Check if the <Mode> may be in the mode string.
89   const auto *ModeStr = dyn_cast<StringLiteral>(ModeArg->IgnoreParenCasts());
90   if (!ModeStr || (ModeStr->getString().find(Mode) != StringRef::npos))
91     return;
92 
93   std::string ReplacementText = buildFixMsgForStringFlag(
94       ModeArg, *Result.SourceManager, Result.Context->getLangOpts(), Mode);
95 
96   diag(ModeArg->getBeginLoc(), "use %0 mode '%1' to set O_CLOEXEC")
97       << FD << std::string(1, Mode)
98       << FixItHint::CreateReplacement(ModeArg->getSourceRange(),
99                                       ReplacementText);
100 }
101 
getSpellingArg(const MatchFinder::MatchResult & Result,int N) const102 StringRef CloexecCheck::getSpellingArg(const MatchFinder::MatchResult &Result,
103                                        int N) const {
104   const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
105   const SourceManager &SM = *Result.SourceManager;
106   return Lexer::getSourceText(
107       CharSourceRange::getTokenRange(MatchedCall->getArg(N)->getSourceRange()),
108       SM, Result.Context->getLangOpts());
109 }
110 
111 } // namespace android
112 } // namespace tidy
113 } // namespace clang
114