//===--- UnusedReturnValueCheck.cpp - clang-tidy---------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "UnusedReturnValueCheck.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; using namespace clang::ast_matchers::internal; namespace clang { namespace tidy { namespace bugprone { namespace { // Matches functions that are instantiated from a class template member function // matching InnerMatcher. Functions not instantiated from a class template // member function are matched directly with InnerMatcher. AST_MATCHER_P(FunctionDecl, isInstantiatedFrom, Matcher, InnerMatcher) { FunctionDecl *InstantiatedFrom = Node.getInstantiatedFromMemberFunction(); return InnerMatcher.matches(InstantiatedFrom ? *InstantiatedFrom : Node, Finder, Builder); } } // namespace UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), CheckedFunctions(Options.get("CheckedFunctions", "::std::async;" "::std::launder;" "::std::remove;" "::std::remove_if;" "::std::unique;" "::std::unique_ptr::release;" "::std::basic_string::empty;" "::std::vector::empty;" "::std::back_inserter;" "::std::distance;" "::std::find;" "::std::find_if;" "::std::inserter;" "::std::lower_bound;" "::std::make_pair;" "::std::map::count;" "::std::map::find;" "::std::map::lower_bound;" "::std::multimap::equal_range;" "::std::multimap::upper_bound;" "::std::set::count;" "::std::set::find;" "::std::setfill;" "::std::setprecision;" "::std::setw;" "::std::upper_bound;" "::std::vector::at;" // C standard library "::bsearch;" "::ferror;" "::feof;" "::isalnum;" "::isalpha;" "::isblank;" "::iscntrl;" "::isdigit;" "::isgraph;" "::islower;" "::isprint;" "::ispunct;" "::isspace;" "::isupper;" "::iswalnum;" "::iswprint;" "::iswspace;" "::isxdigit;" "::memchr;" "::memcmp;" "::strcmp;" "::strcoll;" "::strncmp;" "::strpbrk;" "::strrchr;" "::strspn;" "::strstr;" "::wcscmp;" // POSIX "::access;" "::bind;" "::connect;" "::difftime;" "::dlsym;" "::fnmatch;" "::getaddrinfo;" "::getopt;" "::htonl;" "::htons;" "::iconv_open;" "::inet_addr;" "::isascii;" "::isatty;" "::mmap;" "::newlocale;" "::openat;" "::pathconf;" "::pthread_equal;" "::pthread_getspecific;" "::pthread_mutex_trylock;" "::readdir;" "::readlink;" "::recvmsg;" "::regexec;" "::scandir;" "::semget;" "::setjmp;" "::shm_open;" "::shmget;" "::sigismember;" "::strcasecmp;" "::strsignal;" "::ttyname")) {} void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "CheckedFunctions", CheckedFunctions); } void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) { auto FunVec = utils::options::parseStringList(CheckedFunctions); auto MatchedCallExpr = expr(ignoringImplicit(ignoringParenImpCasts( callExpr(callee(functionDecl( // Don't match void overloads of checked functions. unless(returns(voidType())), isInstantiatedFrom(hasAnyName( std::vector(FunVec.begin(), FunVec.end())))))) .bind("match")))); auto UnusedInCompoundStmt = compoundStmt(forEach(MatchedCallExpr), // The checker can't currently differentiate between the // return statement and other statements inside GNU statement // expressions, so disable the checker inside them to avoid // false positives. unless(hasParent(stmtExpr()))); auto UnusedInIfStmt = ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr))); auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr)); auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr)); auto UnusedInForStmt = forStmt(eachOf(hasLoopInit(MatchedCallExpr), hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr))); auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr)); auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr)); Finder->addMatcher( stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt, UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt, UnusedInCaseStmt)), this); } void UnusedReturnValueCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *Matched = Result.Nodes.getNodeAs("match")) { diag(Matched->getBeginLoc(), "the value returned by this function should be used") << Matched->getSourceRange(); diag(Matched->getBeginLoc(), "cast the expression to void to silence this warning", DiagnosticIDs::Note); } } } // namespace bugprone } // namespace tidy } // namespace clang