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