xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp (revision 507c18b445ef88d985d95181db8107f669aed998)
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/Matchers.h"
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/Basic/OperatorKinds.h"
16 
17 using namespace clang::ast_matchers;
18 using namespace clang::ast_matchers::internal;
19 
20 namespace clang::tidy::bugprone {
21 
22 namespace {
23 
24 // Matches functions that are instantiated from a class template member function
25 // matching InnerMatcher. Functions not instantiated from a class template
26 // member function are matched directly with InnerMatcher.
27 AST_MATCHER_P(FunctionDecl, isInstantiatedFrom, Matcher<FunctionDecl>,
28               InnerMatcher) {
29   FunctionDecl *InstantiatedFrom = Node.getInstantiatedFromMemberFunction();
30   return InnerMatcher.matches(InstantiatedFrom ? *InstantiatedFrom : Node,
31                               Finder, Builder);
32 }
33 
34 constexpr std::initializer_list<OverloadedOperatorKind>
35     AssignmentOverloadedOperatorKinds = {
36         OO_Equal,      OO_PlusEqual,     OO_MinusEqual,          OO_StarEqual,
37         OO_SlashEqual, OO_PercentEqual,  OO_CaretEqual,          OO_AmpEqual,
38         OO_PipeEqual,  OO_LessLessEqual, OO_GreaterGreaterEqual, OO_PlusPlus,
39         OO_MinusMinus};
40 
41 AST_MATCHER(FunctionDecl, isAssignmentOverloadedOperator) {
42   return llvm::is_contained(AssignmentOverloadedOperatorKinds,
43                             Node.getOverloadedOperator());
44 }
45 } // namespace
46 
47 UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name,
48                                                ClangTidyContext *Context)
49     : ClangTidyCheck(Name, Context),
50       CheckedFunctions(utils::options::parseStringList(
51           Options.get("CheckedFunctions", "^::std::async$;"
52                                           "^::std::launder$;"
53                                           "^::std::remove$;"
54                                           "^::std::remove_if$;"
55                                           "^::std::unique$;"
56                                           "^::std::unique_ptr::release$;"
57                                           "^::std::basic_string::empty$;"
58                                           "^::std::vector::empty$;"
59                                           "^::std::back_inserter$;"
60                                           "^::std::distance$;"
61                                           "^::std::find$;"
62                                           "^::std::find_if$;"
63                                           "^::std::inserter$;"
64                                           "^::std::lower_bound$;"
65                                           "^::std::make_pair$;"
66                                           "^::std::map::count$;"
67                                           "^::std::map::find$;"
68                                           "^::std::map::lower_bound$;"
69                                           "^::std::multimap::equal_range$;"
70                                           "^::std::multimap::upper_bound$;"
71                                           "^::std::set::count$;"
72                                           "^::std::set::find$;"
73                                           "^::std::setfill$;"
74                                           "^::std::setprecision$;"
75                                           "^::std::setw$;"
76                                           "^::std::upper_bound$;"
77                                           "^::std::vector::at$;"
78                                           // C standard library
79                                           "^::bsearch$;"
80                                           "^::ferror$;"
81                                           "^::feof$;"
82                                           "^::isalnum$;"
83                                           "^::isalpha$;"
84                                           "^::isblank$;"
85                                           "^::iscntrl$;"
86                                           "^::isdigit$;"
87                                           "^::isgraph$;"
88                                           "^::islower$;"
89                                           "^::isprint$;"
90                                           "^::ispunct$;"
91                                           "^::isspace$;"
92                                           "^::isupper$;"
93                                           "^::iswalnum$;"
94                                           "^::iswprint$;"
95                                           "^::iswspace$;"
96                                           "^::isxdigit$;"
97                                           "^::memchr$;"
98                                           "^::memcmp$;"
99                                           "^::strcmp$;"
100                                           "^::strcoll$;"
101                                           "^::strncmp$;"
102                                           "^::strpbrk$;"
103                                           "^::strrchr$;"
104                                           "^::strspn$;"
105                                           "^::strstr$;"
106                                           "^::wcscmp$;"
107                                           // POSIX
108                                           "^::access$;"
109                                           "^::bind$;"
110                                           "^::connect$;"
111                                           "^::difftime$;"
112                                           "^::dlsym$;"
113                                           "^::fnmatch$;"
114                                           "^::getaddrinfo$;"
115                                           "^::getopt$;"
116                                           "^::htonl$;"
117                                           "^::htons$;"
118                                           "^::iconv_open$;"
119                                           "^::inet_addr$;"
120                                           "^::isascii$;"
121                                           "^::isatty$;"
122                                           "^::mmap$;"
123                                           "^::newlocale$;"
124                                           "^::openat$;"
125                                           "^::pathconf$;"
126                                           "^::pthread_equal$;"
127                                           "^::pthread_getspecific$;"
128                                           "^::pthread_mutex_trylock$;"
129                                           "^::readdir$;"
130                                           "^::readlink$;"
131                                           "^::recvmsg$;"
132                                           "^::regexec$;"
133                                           "^::scandir$;"
134                                           "^::semget$;"
135                                           "^::setjmp$;"
136                                           "^::shm_open$;"
137                                           "^::shmget$;"
138                                           "^::sigismember$;"
139                                           "^::strcasecmp$;"
140                                           "^::strsignal$;"
141                                           "^::ttyname$"))),
142       CheckedReturnTypes(utils::options::parseStringList(
143           Options.get("CheckedReturnTypes", "^::std::error_code$;"
144                                             "^::std::error_condition$;"
145                                             "^::std::errc$;"
146                                             "^::std::expected$;"
147                                             "^::boost::system::error_code$"))),
148       AllowCastToVoid(Options.get("AllowCastToVoid", false)) {}
149 
150 UnusedReturnValueCheck::UnusedReturnValueCheck(
151     llvm::StringRef Name, ClangTidyContext *Context,
152     std::vector<StringRef> CheckedFunctions)
153     : UnusedReturnValueCheck(Name, Context, std::move(CheckedFunctions), {},
154                              false) {}
155 
156 UnusedReturnValueCheck::UnusedReturnValueCheck(
157     llvm::StringRef Name, ClangTidyContext *Context,
158     std::vector<StringRef> CheckedFunctions,
159     std::vector<StringRef> CheckedReturnTypes, bool AllowCastToVoid)
160     : ClangTidyCheck(Name, Context),
161       CheckedFunctions(std::move(CheckedFunctions)),
162       CheckedReturnTypes(std::move(CheckedReturnTypes)),
163       AllowCastToVoid(AllowCastToVoid) {}
164 
165 void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
166   Options.store(Opts, "CheckedFunctions",
167                 utils::options::serializeStringList(CheckedFunctions));
168   Options.store(Opts, "CheckedReturnTypes",
169                 utils::options::serializeStringList(CheckedReturnTypes));
170   Options.store(Opts, "AllowCastToVoid", AllowCastToVoid);
171 }
172 
173 void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) {
174   auto MatchedDirectCallExpr =
175       expr(callExpr(callee(functionDecl(
176                         // Don't match copy or move assignment operator.
177                         unless(isAssignmentOverloadedOperator()),
178                         // Don't match void overloads of checked functions.
179                         unless(returns(voidType())),
180                         anyOf(isInstantiatedFrom(matchers::matchesAnyListedName(
181                                   CheckedFunctions)),
182                               returns(hasCanonicalType(hasDeclaration(
183                                   namedDecl(matchers::matchesAnyListedName(
184                                       CheckedReturnTypes)))))))))
185                .bind("match"));
186 
187   auto CheckCastToVoid =
188       AllowCastToVoid ? castExpr(unless(hasCastKind(CK_ToVoid))) : castExpr();
189   auto MatchedCallExpr = expr(
190       anyOf(MatchedDirectCallExpr,
191             explicitCastExpr(unless(cxxFunctionalCastExpr()), CheckCastToVoid,
192                              hasSourceExpression(MatchedDirectCallExpr))));
193 
194   auto UnusedInCompoundStmt =
195       compoundStmt(forEach(MatchedCallExpr),
196                    // The checker can't currently differentiate between the
197                    // return statement and other statements inside GNU statement
198                    // expressions, so disable the checker inside them to avoid
199                    // false positives.
200                    unless(hasParent(stmtExpr())));
201   auto UnusedInIfStmt =
202       ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr)));
203   auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr));
204   auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr));
205   auto UnusedInForStmt =
206       forStmt(eachOf(hasLoopInit(MatchedCallExpr),
207                      hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr)));
208   auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr));
209   auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr));
210 
211   Finder->addMatcher(
212       stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,
213                  UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,
214                  UnusedInCaseStmt)),
215       this);
216 }
217 
218 void UnusedReturnValueCheck::check(const MatchFinder::MatchResult &Result) {
219   if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("match")) {
220     diag(Matched->getBeginLoc(),
221          "the value returned by this function should not be disregarded; "
222          "neglecting it may lead to errors")
223         << Matched->getSourceRange();
224 
225     if (!AllowCastToVoid)
226       return;
227 
228     diag(Matched->getBeginLoc(),
229          "cast the expression to void to silence this warning",
230          DiagnosticIDs::Note);
231   }
232 }
233 
234 } // namespace clang::tidy::bugprone
235