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

source code of clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp