1 //===--- UnusedReturnValueCheck.cpp - clang-tidy---------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
17 using namespace clang::ast_matchers
;
18 using namespace clang::ast_matchers::internal
;
20 namespace clang::tidy::bugprone
{
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
>,
29 FunctionDecl
*InstantiatedFrom
= Node
.getInstantiatedFromMemberFunction();
30 return InnerMatcher
.matches(InstantiatedFrom
? *InstantiatedFrom
: Node
,
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
,
41 AST_MATCHER(FunctionDecl
, isAssignmentOverloadedOperator
) {
42 return llvm::is_contained(AssignmentOverloadedOperatorKinds
,
43 Node
.getOverloadedOperator());
47 UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name
,
48 ClangTidyContext
*Context
)
49 : ClangTidyCheck(Name
, Context
),
50 CheckedFunctions(utils::options::parseStringList(
51 Options
.get("CheckedFunctions", "^::std::async$;"
56 "^::std::unique_ptr::release$;"
57 "^::std::basic_string::empty$;"
58 "^::std::vector::empty$;"
59 "^::std::back_inserter$;"
64 "^::std::lower_bound$;"
66 "^::std::map::count$;"
68 "^::std::map::lower_bound$;"
69 "^::std::multimap::equal_range$;"
70 "^::std::multimap::upper_bound$;"
71 "^::std::set::count$;"
74 "^::std::setprecision$;"
76 "^::std::upper_bound$;"
77 "^::std::vector::at$;"
127 "^::pthread_getspecific$;"
128 "^::pthread_mutex_trylock$;"
142 CheckedReturnTypes(utils::options::parseStringList(
143 Options
.get("CheckedReturnTypes", "^::std::error_code$;"
144 "^::std::error_condition$;"
147 "^::boost::system::error_code$"))),
148 AllowCastToVoid(Options
.get("AllowCastToVoid", false)) {}
150 UnusedReturnValueCheck::UnusedReturnValueCheck(
151 llvm::StringRef Name
, ClangTidyContext
*Context
,
152 std::vector
<StringRef
> CheckedFunctions
)
153 : UnusedReturnValueCheck(Name
, Context
, std::move(CheckedFunctions
), {},
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
) {}
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
);
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(
182 returns(hasCanonicalType(hasDeclaration(
183 namedDecl(matchers::matchesAnyListedName(
184 CheckedReturnTypes
)))))))))
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
))));
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
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
));
212 stmt(anyOf(UnusedInCompoundStmt
, UnusedInIfStmt
, UnusedInWhileStmt
,
213 UnusedInDoStmt
, UnusedInForStmt
, UnusedInRangeForStmt
,
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();
225 if (!AllowCastToVoid
)
228 diag(Matched
->getBeginLoc(),
229 "cast the expression to void to silence this warning",
230 DiagnosticIDs::Note
);
234 } // namespace clang::tidy::bugprone