xref: /llvm-project/clang-tools-extra/clang-tidy/android/CloexecOpenCheck.cpp (revision 600a6133ad65fa5f0ef9d5aad4b353e2bc32cedc)
1*600a6133SYan Wang //===--- CloexecOpenCheck.cpp - clang-tidy---------------------------------===//
2*600a6133SYan Wang //
3*600a6133SYan Wang //                     The LLVM Compiler Infrastructure
4*600a6133SYan Wang //
5*600a6133SYan Wang // This file is distributed under the University of Illinois Open Source
6*600a6133SYan Wang // License. See LICENSE.TXT for details.
7*600a6133SYan Wang //
8*600a6133SYan Wang //===----------------------------------------------------------------------===//
9*600a6133SYan Wang 
10*600a6133SYan Wang #include "CloexecOpenCheck.h"
11*600a6133SYan Wang #include "clang/AST/ASTContext.h"
12*600a6133SYan Wang #include "clang/ASTMatchers/ASTMatchFinder.h"
13*600a6133SYan Wang #include "clang/Lex/Lexer.h"
14*600a6133SYan Wang 
15*600a6133SYan Wang using namespace clang::ast_matchers;
16*600a6133SYan Wang 
17*600a6133SYan Wang namespace clang {
18*600a6133SYan Wang namespace tidy {
19*600a6133SYan Wang namespace android {
20*600a6133SYan Wang 
21*600a6133SYan Wang namespace {
22*600a6133SYan Wang static constexpr const char *O_CLOEXEC = "O_CLOEXEC";
23*600a6133SYan Wang 
24*600a6133SYan Wang bool hasCloseOnExecFlag(const Expr *Flags, const SourceManager &SM,
25*600a6133SYan Wang                         const LangOptions &LangOpts) {
26*600a6133SYan Wang   // If the Flag is an integer constant, check it.
27*600a6133SYan Wang   if (isa<IntegerLiteral>(Flags)) {
28*600a6133SYan Wang     if (!SM.isMacroBodyExpansion(Flags->getLocStart()) &&
29*600a6133SYan Wang         !SM.isMacroArgExpansion(Flags->getLocStart()))
30*600a6133SYan Wang       return false;
31*600a6133SYan Wang 
32*600a6133SYan Wang     // Get the Marco name.
33*600a6133SYan Wang     auto MacroName = Lexer::getSourceText(
34*600a6133SYan Wang         CharSourceRange::getTokenRange(Flags->getSourceRange()), SM, LangOpts);
35*600a6133SYan Wang 
36*600a6133SYan Wang     return MacroName == O_CLOEXEC;
37*600a6133SYan Wang   }
38*600a6133SYan Wang   // If it's a binary OR operation.
39*600a6133SYan Wang   if (const auto *BO = dyn_cast<BinaryOperator>(Flags))
40*600a6133SYan Wang     if (BO->getOpcode() == clang::BinaryOperatorKind::BO_Or)
41*600a6133SYan Wang       return hasCloseOnExecFlag(BO->getLHS()->IgnoreParenCasts(), SM,
42*600a6133SYan Wang                                 LangOpts) ||
43*600a6133SYan Wang              hasCloseOnExecFlag(BO->getRHS()->IgnoreParenCasts(), SM, LangOpts);
44*600a6133SYan Wang 
45*600a6133SYan Wang   // Otherwise, assume it has the flag.
46*600a6133SYan Wang   return true;
47*600a6133SYan Wang }
48*600a6133SYan Wang } // namespace
49*600a6133SYan Wang 
50*600a6133SYan Wang void CloexecOpenCheck::registerMatchers(MatchFinder *Finder) {
51*600a6133SYan Wang   auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
52*600a6133SYan Wang 
53*600a6133SYan Wang   Finder->addMatcher(
54*600a6133SYan Wang       callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
55*600a6133SYan Wang                                    hasAnyName("open", "open64"),
56*600a6133SYan Wang                                    hasParameter(0, CharPointerType),
57*600a6133SYan Wang                                    hasParameter(1, hasType(isInteger())))
58*600a6133SYan Wang                           .bind("funcDecl")))
59*600a6133SYan Wang           .bind("openFn"),
60*600a6133SYan Wang       this);
61*600a6133SYan Wang   Finder->addMatcher(
62*600a6133SYan Wang       callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
63*600a6133SYan Wang                                    hasName("openat"),
64*600a6133SYan Wang                                    hasParameter(0, hasType(isInteger())),
65*600a6133SYan Wang                                    hasParameter(1, CharPointerType),
66*600a6133SYan Wang                                    hasParameter(2, hasType(isInteger())))
67*600a6133SYan Wang                           .bind("funcDecl")))
68*600a6133SYan Wang           .bind("openatFn"),
69*600a6133SYan Wang       this);
70*600a6133SYan Wang }
71*600a6133SYan Wang 
72*600a6133SYan Wang void CloexecOpenCheck::check(const MatchFinder::MatchResult &Result) {
73*600a6133SYan Wang   const Expr *FlagArg = nullptr;
74*600a6133SYan Wang   if (const auto *OpenFnCall = Result.Nodes.getNodeAs<CallExpr>("openFn"))
75*600a6133SYan Wang     FlagArg = OpenFnCall->getArg(1);
76*600a6133SYan Wang   else if (const auto *OpenFnCall =
77*600a6133SYan Wang                Result.Nodes.getNodeAs<CallExpr>("openatFn"))
78*600a6133SYan Wang     FlagArg = OpenFnCall->getArg(2);
79*600a6133SYan Wang   assert(FlagArg);
80*600a6133SYan Wang 
81*600a6133SYan Wang   const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
82*600a6133SYan Wang 
83*600a6133SYan Wang   // Check the required flag.
84*600a6133SYan Wang   SourceManager &SM = *Result.SourceManager;
85*600a6133SYan Wang   if (hasCloseOnExecFlag(FlagArg->IgnoreParenCasts(), SM,
86*600a6133SYan Wang                          Result.Context->getLangOpts()))
87*600a6133SYan Wang     return;
88*600a6133SYan Wang 
89*600a6133SYan Wang   SourceLocation EndLoc =
90*600a6133SYan Wang       Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
91*600a6133SYan Wang                                  Result.Context->getLangOpts());
92*600a6133SYan Wang 
93*600a6133SYan Wang   diag(EndLoc, "%0 should use %1 where possible")
94*600a6133SYan Wang       << FD << O_CLOEXEC
95*600a6133SYan Wang       << FixItHint::CreateInsertion(EndLoc, (Twine(" | ") + O_CLOEXEC).str());
96*600a6133SYan Wang }
97*600a6133SYan Wang 
98*600a6133SYan Wang } // namespace android
99*600a6133SYan Wang } // namespace tidy
100*600a6133SYan Wang } // namespace clang
101