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