• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- UnusedReturnValueCheck.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 "UnusedReturnValueCheck.h"
10 #include "../utils/OptionsUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 using namespace clang::ast_matchers::internal;
16 
17 namespace clang {
18 namespace tidy {
19 namespace bugprone {
20 
21 namespace {
22 
23 // Matches functions that are instantiated from a class template member function
24 // matching InnerMatcher. Functions not instantiated from a class template
25 // member function are matched directly with InnerMatcher.
AST_MATCHER_P(FunctionDecl,isInstantiatedFrom,Matcher<FunctionDecl>,InnerMatcher)26 AST_MATCHER_P(FunctionDecl, isInstantiatedFrom, Matcher<FunctionDecl>,
27               InnerMatcher) {
28   FunctionDecl *InstantiatedFrom = Node.getInstantiatedFromMemberFunction();
29   return InnerMatcher.matches(InstantiatedFrom ? *InstantiatedFrom : Node,
30                               Finder, Builder);
31 }
32 
33 } // namespace
34 
UnusedReturnValueCheck(llvm::StringRef Name,ClangTidyContext * Context)35 UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name,
36                                                ClangTidyContext *Context)
37     : ClangTidyCheck(Name, Context),
38       CheckedFunctions(Options.get("CheckedFunctions",
39                                    "::std::async;"
40                                    "::std::launder;"
41                                    "::std::remove;"
42                                    "::std::remove_if;"
43                                    "::std::unique;"
44                                    "::std::unique_ptr::release;"
45                                    "::std::basic_string::empty;"
46                                    "::std::vector::empty;"
47                                    "::std::back_inserter;"
48                                    "::std::distance;"
49                                    "::std::find;"
50                                    "::std::find_if;"
51                                    "::std::inserter;"
52                                    "::std::lower_bound;"
53                                    "::std::make_pair;"
54                                    "::std::map::count;"
55                                    "::std::map::find;"
56                                    "::std::map::lower_bound;"
57                                    "::std::multimap::equal_range;"
58                                    "::std::multimap::upper_bound;"
59                                    "::std::set::count;"
60                                    "::std::set::find;"
61                                    "::std::setfill;"
62                                    "::std::setprecision;"
63                                    "::std::setw;"
64                                    "::std::upper_bound;"
65                                    "::std::vector::at;"
66                                    // C standard library
67                                    "::bsearch;"
68                                    "::ferror;"
69                                    "::feof;"
70                                    "::isalnum;"
71                                    "::isalpha;"
72                                    "::isblank;"
73                                    "::iscntrl;"
74                                    "::isdigit;"
75                                    "::isgraph;"
76                                    "::islower;"
77                                    "::isprint;"
78                                    "::ispunct;"
79                                    "::isspace;"
80                                    "::isupper;"
81                                    "::iswalnum;"
82                                    "::iswprint;"
83                                    "::iswspace;"
84                                    "::isxdigit;"
85                                    "::memchr;"
86                                    "::memcmp;"
87                                    "::strcmp;"
88                                    "::strcoll;"
89                                    "::strncmp;"
90                                    "::strpbrk;"
91                                    "::strrchr;"
92                                    "::strspn;"
93                                    "::strstr;"
94                                    "::wcscmp;"
95                                    // POSIX
96                                    "::access;"
97                                    "::bind;"
98                                    "::connect;"
99                                    "::difftime;"
100                                    "::dlsym;"
101                                    "::fnmatch;"
102                                    "::getaddrinfo;"
103                                    "::getopt;"
104                                    "::htonl;"
105                                    "::htons;"
106                                    "::iconv_open;"
107                                    "::inet_addr;"
108                                    "::isascii;"
109                                    "::isatty;"
110                                    "::mmap;"
111                                    "::newlocale;"
112                                    "::openat;"
113                                    "::pathconf;"
114                                    "::pthread_equal;"
115                                    "::pthread_getspecific;"
116                                    "::pthread_mutex_trylock;"
117                                    "::readdir;"
118                                    "::readlink;"
119                                    "::recvmsg;"
120                                    "::regexec;"
121                                    "::scandir;"
122                                    "::semget;"
123                                    "::setjmp;"
124                                    "::shm_open;"
125                                    "::shmget;"
126                                    "::sigismember;"
127                                    "::strcasecmp;"
128                                    "::strsignal;"
129                                    "::ttyname")) {}
130 
storeOptions(ClangTidyOptions::OptionMap & Opts)131 void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
132   Options.store(Opts, "CheckedFunctions", CheckedFunctions);
133 }
134 
registerMatchers(MatchFinder * Finder)135 void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) {
136   auto FunVec = utils::options::parseStringList(CheckedFunctions);
137   auto MatchedCallExpr = expr(ignoringImplicit(ignoringParenImpCasts(
138       callExpr(callee(functionDecl(
139                    // Don't match void overloads of checked functions.
140                    unless(returns(voidType())),
141                    isInstantiatedFrom(hasAnyName(
142                        std::vector<StringRef>(FunVec.begin(), FunVec.end()))))))
143           .bind("match"))));
144 
145   auto UnusedInCompoundStmt =
146       compoundStmt(forEach(MatchedCallExpr),
147                    // The checker can't currently differentiate between the
148                    // return statement and other statements inside GNU statement
149                    // expressions, so disable the checker inside them to avoid
150                    // false positives.
151                    unless(hasParent(stmtExpr())));
152   auto UnusedInIfStmt =
153       ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr)));
154   auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr));
155   auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr));
156   auto UnusedInForStmt =
157       forStmt(eachOf(hasLoopInit(MatchedCallExpr),
158                      hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr)));
159   auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr));
160   auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr));
161 
162   Finder->addMatcher(
163       stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,
164                  UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,
165                  UnusedInCaseStmt)),
166       this);
167 }
168 
check(const MatchFinder::MatchResult & Result)169 void UnusedReturnValueCheck::check(const MatchFinder::MatchResult &Result) {
170   if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("match")) {
171     diag(Matched->getBeginLoc(),
172          "the value returned by this function should be used")
173         << Matched->getSourceRange();
174     diag(Matched->getBeginLoc(),
175          "cast the expression to void to silence this warning",
176          DiagnosticIDs::Note);
177   }
178 }
179 
180 } // namespace bugprone
181 } // namespace tidy
182 } // namespace clang
183