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