235 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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 function24// matching InnerMatcher. Functions not instantiated from a class template25// member function are matched directly with InnerMatcher.26AST_MATCHER_P(FunctionDecl, isInstantiatedFrom, Matcher<FunctionDecl>,27 InnerMatcher) {28 const FunctionDecl *InstantiatedFrom =29 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} // namespace46 47UnusedReturnValueCheck::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 library79 "^::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 // POSIX108 "^::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 the197 // return statement and other statements inside GNU statement198 // expressions, so disable the checker inside them to avoid199 // 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::bugprone235