clang-tools 19.0.0git
UnusedReturnValueCheck.cpp
Go to the documentation of this file.
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
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
17using namespace clang::ast_matchers;
18using namespace clang::ast_matchers::internal;
19
20namespace clang::tidy::bugprone {
21
22namespace {
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.
27AST_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
34constexpr 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
41AST_MATCHER(FunctionDecl, isAssignmentOverloadedOperator) {
42 return llvm::is_contained(AssignmentOverloadedOperatorKinds,
43 Node.getOverloadedOperator());
44}
45} // namespace
46
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
150UnusedReturnValueCheck::UnusedReturnValueCheck(
151 llvm::StringRef Name, ClangTidyContext *Context,
152 std::vector<StringRef> CheckedFunctions)
153 : UnusedReturnValueCheck(Name, Context, std::move(CheckedFunctions), {},
154 false) {}
155
156UnusedReturnValueCheck::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
165void 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
173void 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
218void 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
llvm::SmallString< 256U > Name
CodeCompletionBuilder Builder
::clang::DynTypedNode Node
UnusedReturnValueCheck(StringRef Name, ClangTidyContext *Context)
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
AST_MATCHER(clang::VarDecl, hasConstantDeclaration)
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
llvm::StringMap< ClangTidyValue > OptionMap