xref: /llvm-project/clang-tools-extra/clang-tidy/android/CloexecCheck.cpp (revision 7d2ea6c422d3f5712b7253407005e1a465a76946)
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::tidy::android {
18 
19 namespace {
20 // Helper function to form the correct string mode for Type3.
21 // Build the replace text. If it's string constant, add <Mode> directly in the
22 // end of the string. Else, add <Mode>.
buildFixMsgForStringFlag(const Expr * Arg,const SourceManager & SM,const LangOptions & LangOpts,char Mode)23 std::string buildFixMsgForStringFlag(const Expr *Arg, const SourceManager &SM,
24                                      const LangOptions &LangOpts, char Mode) {
25   if (Arg->getBeginLoc().isMacroID())
26     return (Lexer::getSourceText(
27                 CharSourceRange::getTokenRange(Arg->getSourceRange()), SM,
28                 LangOpts) +
29             " \"" + Twine(Mode) + "\"")
30         .str();
31 
32   StringRef SR = cast<StringLiteral>(Arg->IgnoreParenCasts())->getString();
33   return ("\"" + SR + Twine(Mode) + "\"").str();
34 }
35 } // namespace
36 
37 const char *CloexecCheck::FuncDeclBindingStr = "funcDecl";
38 
39 const char *CloexecCheck::FuncBindingStr ="func";
40 
registerMatchersImpl(MatchFinder * Finder,internal::Matcher<FunctionDecl> Function)41 void CloexecCheck::registerMatchersImpl(
42     MatchFinder *Finder, internal::Matcher<FunctionDecl> Function) {
43   // We assume all the checked APIs are C functions.
44   Finder->addMatcher(
45       callExpr(
46           callee(functionDecl(isExternC(), Function).bind(FuncDeclBindingStr)))
47           .bind(FuncBindingStr),
48       this);
49 }
50 
insertMacroFlag(const MatchFinder::MatchResult & Result,StringRef MacroFlag,int ArgPos)51 void CloexecCheck::insertMacroFlag(const MatchFinder::MatchResult &Result,
52                                    StringRef MacroFlag, int ArgPos) {
53   const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
54   const auto *FlagArg = MatchedCall->getArg(ArgPos);
55   const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(FuncDeclBindingStr);
56   SourceManager &SM = *Result.SourceManager;
57 
58   if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
59                                         Result.Context->getLangOpts(),
60                                         MacroFlag))
61     return;
62 
63   SourceLocation EndLoc =
64       Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getEndLoc()), 0, SM,
65                                  Result.Context->getLangOpts());
66 
67   diag(EndLoc, "%0 should use %1 where possible")
68       << FD << MacroFlag
69       << FixItHint::CreateInsertion(EndLoc, (Twine(" | ") + MacroFlag).str());
70 }
71 
replaceFunc(const MatchFinder::MatchResult & Result,StringRef WarningMsg,StringRef FixMsg)72 void CloexecCheck::replaceFunc(const MatchFinder::MatchResult &Result,
73                                StringRef WarningMsg, StringRef FixMsg) {
74   const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
75   diag(MatchedCall->getBeginLoc(), WarningMsg)
76       << FixItHint::CreateReplacement(MatchedCall->getSourceRange(), FixMsg);
77 }
78 
insertStringFlag(const ast_matchers::MatchFinder::MatchResult & Result,const char Mode,const int ArgPos)79 void CloexecCheck::insertStringFlag(
80     const ast_matchers::MatchFinder::MatchResult &Result, const char Mode,
81     const int ArgPos) {
82   const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
83   const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>(FuncDeclBindingStr);
84   const auto *ModeArg = MatchedCall->getArg(ArgPos);
85 
86   // Check if the <Mode> may be in the mode string.
87   const auto *ModeStr = dyn_cast<StringLiteral>(ModeArg->IgnoreParenCasts());
88   if (!ModeStr || ModeStr->getString().contains(Mode))
89     return;
90 
91   std::string ReplacementText = buildFixMsgForStringFlag(
92       ModeArg, *Result.SourceManager, Result.Context->getLangOpts(), Mode);
93 
94   diag(ModeArg->getBeginLoc(), "use %0 mode '%1' to set O_CLOEXEC")
95       << FD << std::string(1, Mode)
96       << FixItHint::CreateReplacement(ModeArg->getSourceRange(),
97                                       ReplacementText);
98 }
99 
getSpellingArg(const MatchFinder::MatchResult & Result,int N) const100 StringRef CloexecCheck::getSpellingArg(const MatchFinder::MatchResult &Result,
101                                        int N) const {
102   const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>(FuncBindingStr);
103   const SourceManager &SM = *Result.SourceManager;
104   return Lexer::getSourceText(
105       CharSourceRange::getTokenRange(MatchedCall->getArg(N)->getSourceRange()),
106       SM, Result.Context->getLangOpts());
107 }
108 
109 } // namespace clang::tidy::android
110