1 //===--- NonTrivialTypesLibcMemoryCallsCheck.cpp - clang-tidy ----------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "NonTrivialTypesLibcMemoryCallsCheck.h"
10 #include "../utils/OptionsUtils.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/ASTMatchers/ASTMatchersInternal.h"
15 #include "clang/ASTMatchers/ASTMatchersMacros.h"
16 #include "llvm/ADT/SmallVector.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/raw_ostream.h"
20 using namespace clang::ast_matchers
;
22 namespace clang::tidy::cert
{
25 AST_MATCHER(CXXRecordDecl
, isTriviallyDefaultConstructible
) {
26 return Node
.hasTrivialDefaultConstructor();
28 AST_MATCHER(CXXRecordDecl
, isTriviallyCopyable
) {
29 return Node
.hasTrivialCopyAssignment() && Node
.hasTrivialCopyConstructor();
33 static const char BuiltinMemSet
[] = "::std::memset;"
35 static const char BuiltinMemCpy
[] = "::std::memcpy;"
44 static const char BuiltinMemCmp
[] = "::std::memcmp;"
49 static constexpr llvm::StringRef ComparisonOperators
[] = {
50 "operator==", "operator!=", "operator<",
51 "operator>", "operator<=", "operator>="};
53 NonTrivialTypesLibcMemoryCallsCheck::NonTrivialTypesLibcMemoryCallsCheck(
54 StringRef Name
, ClangTidyContext
*Context
)
55 : ClangTidyCheck(Name
, Context
),
56 MemSetNames(Options
.get("MemSetNames", "")),
57 MemCpyNames(Options
.get("MemCpyNames", "")),
58 MemCmpNames(Options
.get("MemCmpNames", "")) {}
60 void NonTrivialTypesLibcMemoryCallsCheck::storeOptions(
61 ClangTidyOptions::OptionMap
&Opts
) {
62 Options
.store(Opts
, "MemSetNames", MemSetNames
);
63 Options
.store(Opts
, "MemCpyNames", MemCpyNames
);
64 Options
.store(Opts
, "MemCmpNames", MemCmpNames
);
67 void NonTrivialTypesLibcMemoryCallsCheck::registerMatchers(
68 MatchFinder
*Finder
) {
69 using namespace ast_matchers::internal
;
70 auto IsStructPointer
= [](Matcher
<CXXRecordDecl
> Constraint
= anything(),
72 return expr(unaryOperator(
74 hasUnaryOperand(declRefExpr(
75 hasType(cxxRecordDecl(Constraint
)),
76 hasType(Bind
? qualType().bind("Record") : qualType())))));
79 expr(sizeOfExpr(hasArgumentOfType(equalsBoundNode("Record"))));
80 auto ArgChecker
= [&](Matcher
<CXXRecordDecl
> RecordConstraint
,
81 BindableMatcher
<Stmt
> SecondArg
= expr()) {
82 return allOf(argumentCountIs(3),
83 hasArgument(0, IsStructPointer(RecordConstraint
, true)),
84 hasArgument(1, SecondArg
), hasArgument(2, IsRecordSizeOf
));
88 callExpr(callee(namedDecl(hasAnyName(
89 utils::options::parseListPair(BuiltinMemSet
, MemSetNames
)))),
90 ArgChecker(unless(isTriviallyDefaultConstructible())))
91 .bind("lazyConstruct"),
94 callExpr(callee(namedDecl(hasAnyName(
95 utils::options::parseListPair(BuiltinMemCpy
, MemCpyNames
)))),
96 ArgChecker(unless(isTriviallyCopyable()), IsStructPointer()))
100 callExpr(callee(namedDecl(hasAnyName(
101 utils::options::parseListPair(BuiltinMemCmp
, MemCmpNames
)))),
102 ArgChecker(hasMethod(hasAnyName(ComparisonOperators
)),
104 .bind("lazyCompare"),
108 void NonTrivialTypesLibcMemoryCallsCheck::check(
109 const MatchFinder::MatchResult
&Result
) {
110 if (const auto *Caller
= Result
.Nodes
.getNodeAs
<CallExpr
>("lazyConstruct")) {
111 diag(Caller
->getBeginLoc(), "calling %0 on a non-trivially default "
112 "constructible class is undefined")
113 << cast
<NamedDecl
>(Caller
->getCalleeDecl());
115 if (const auto *Caller
= Result
.Nodes
.getNodeAs
<CallExpr
>("lazyCopy")) {
116 diag(Caller
->getBeginLoc(),
117 "calling %0 on a non-trivially copyable class is undefined")
118 << cast
<NamedDecl
>(Caller
->getCalleeDecl());
120 if (const auto *Caller
= Result
.Nodes
.getNodeAs
<CallExpr
>("lazyCompare")) {
121 diag(Caller
->getBeginLoc(),
122 "consider using comparison operators instead of calling %0")
123 << cast
<NamedDecl
>(Caller
->getCalleeDecl());
127 } // namespace clang::tidy::cert