blob: cb03ad896b6e4be6d806afe0dc1adcb3c844ed17 [file] [log] [blame]
//===--- NonTrivialTypesLibcMemoryCallsCheck.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 "NonTrivialTypesLibcMemoryCallsCheck.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/Decl.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/ASTMatchers/ASTMatchersMacros.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cert {
namespace {
AST_MATCHER(CXXRecordDecl, isTriviallyDefaultConstructible) {
return Node.hasTrivialDefaultConstructor();
}
AST_MATCHER(CXXRecordDecl, isTriviallyCopyable) {
return Node.hasTrivialCopyAssignment() && Node.hasTrivialCopyConstructor();
}
AST_MATCHER_P(NamedDecl, hasAnyNameStdString, std::vector<std::string>,
String) {
return ast_matchers::internal::HasNameMatcher(String).matchesNode(Node);
}
} // namespace
static const char BuiltinMemSet[] = "::std::memset;"
"::memset;";
static const char BuiltinMemCpy[] = "::std::memcpy;"
"::memcpy;"
"::std::memmove;"
"::memmove;"
"::std::strcpy;"
"::strcpy;"
"::memccpy;"
"::stpncpy;"
"::strncpy;";
static const char BuiltinMemCmp[] = "::std::memcmp;"
"::memcmp;"
"::std::strcmp;"
"::strcmp;"
"::strncmp;";
static constexpr llvm::StringRef ComparisonOperators[] = {
"operator==", "operator!=", "operator<",
"operator>", "operator<=", "operator>="};
static std::vector<std::string> parseStringListPair(StringRef LHS,
StringRef RHS) {
if (LHS.empty()) {
if (RHS.empty())
return {};
return utils::options::parseStringList(RHS);
}
if (RHS.empty())
return utils::options::parseStringList(LHS);
llvm::SmallString<512> Buffer;
return utils::options::parseStringList((LHS + RHS).toStringRef(Buffer));
}
NonTrivialTypesLibcMemoryCallsCheck::NonTrivialTypesLibcMemoryCallsCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
MemSetNames(Options.get("MemSetNames", "")),
MemCpyNames(Options.get("MemCpyNames", "")),
MemCmpNames(Options.get("MemCmpNames", "")) {}
void NonTrivialTypesLibcMemoryCallsCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "MemSetNames", MemSetNames);
Options.store(Opts, "MemCpyNames", MemCpyNames);
Options.store(Opts, "MemCmpNames", MemCmpNames);
}
void NonTrivialTypesLibcMemoryCallsCheck::registerMatchers(
MatchFinder *Finder) {
using namespace ast_matchers::internal;
auto IsStructPointer = [](Matcher<CXXRecordDecl> Constraint = anything(),
bool Bind = false) {
return expr(unaryOperator(
hasOperatorName("&"),
hasUnaryOperand(declRefExpr(
allOf(hasType(cxxRecordDecl(Constraint)),
hasType(Bind ? qualType().bind("Record") : qualType()))))));
};
auto IsRecordSizeOf =
expr(sizeOfExpr(hasArgumentOfType(equalsBoundNode("Record"))));
auto ArgChecker = [&](Matcher<CXXRecordDecl> RecordConstraint,
BindableMatcher<Stmt> SecondArg) {
return allOf(argumentCountIs(3),
hasArgument(0, IsStructPointer(RecordConstraint, true)),
hasArgument(1, SecondArg), hasArgument(2, IsRecordSizeOf));
};
Finder->addMatcher(
callExpr(callee(namedDecl(hasAnyNameStdString(
parseStringListPair(BuiltinMemSet, MemSetNames)))),
ArgChecker(unless(isTriviallyDefaultConstructible()),
expr(integerLiteral(equals(0)))))
.bind("lazyConstruct"),
this);
Finder->addMatcher(
callExpr(callee(namedDecl(hasAnyNameStdString(
parseStringListPair(BuiltinMemCpy, MemCpyNames)))),
ArgChecker(unless(isTriviallyCopyable()), IsStructPointer()))
.bind("lazyCopy"),
this);
Finder->addMatcher(
callExpr(callee(namedDecl(hasAnyNameStdString(
parseStringListPair(BuiltinMemCmp, MemCmpNames)))),
ArgChecker(hasMethod(hasAnyName(ComparisonOperators)),
IsStructPointer()))
.bind("lazyCompare"),
this);
}
void NonTrivialTypesLibcMemoryCallsCheck::check(
const MatchFinder::MatchResult &Result) {
if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyConstruct")) {
diag(Caller->getBeginLoc(), "calling %0 on a non-trivially default "
"constructible class is undefined")
<< cast<NamedDecl>(Caller->getCalleeDecl());
}
if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyCopy")) {
diag(Caller->getBeginLoc(),
"calling %0 on a non-trivially copyable class is undefined")
<< cast<NamedDecl>(Caller->getCalleeDecl());
}
if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyCompare")) {
diag(Caller->getBeginLoc(),
"consider using comparison operators instead of calling %0")
<< cast<NamedDecl>(Caller->getCalleeDecl());
}
}
} // namespace cert
} // namespace tidy
} // namespace clang