clang-tools 20.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