blob: d0c588570e1a2f09732ca8e75e6ead7400a6cc5d [file] [log] [blame]
//===--- UnusedReturnValueCheck.cpp - clang-tidy---------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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<FunctionDecl>,
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")) {}
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<StringRef>(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<CallExpr>("match")) {
diag(Matched->getLocStart(),
"the value returned by this function should be used")
<< Matched->getSourceRange();
diag(Matched->getLocStart(),
"cast the expression to void to silence this warning",
DiagnosticIDs::Note);
}
}
} // namespace bugprone
} // namespace tidy
} // namespace clang