1 //===--- NonTrivialTypesLibcMemoryCallsCheck.cpp - clang-tidy ----------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
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"
19
20 using namespace clang::ast_matchers;
21
22 namespace clang {
23 namespace tidy {
24 namespace cert {
25
26 namespace {
AST_MATCHER(CXXRecordDecl,isTriviallyDefaultConstructible)27 AST_MATCHER(CXXRecordDecl, isTriviallyDefaultConstructible) {
28 return Node.hasTrivialDefaultConstructor();
29 }
AST_MATCHER(CXXRecordDecl,isTriviallyCopyable)30 AST_MATCHER(CXXRecordDecl, isTriviallyCopyable) {
31 return Node.hasTrivialCopyAssignment() && Node.hasTrivialCopyConstructor();
32 }
33 } // namespace
34
35 static const char BuiltinMemSet[] = "::std::memset;"
36 "::memset;";
37 static const char BuiltinMemCpy[] = "::std::memcpy;"
38 "::memcpy;"
39 "::std::memmove;"
40 "::memmove;"
41 "::std::strcpy;"
42 "::strcpy;"
43 "::memccpy;"
44 "::stpncpy;"
45 "::strncpy;";
46 static const char BuiltinMemCmp[] = "::std::memcmp;"
47 "::memcmp;"
48 "::std::strcmp;"
49 "::strcmp;"
50 "::strncmp;";
51 static constexpr llvm::StringRef ComparisonOperators[] = {
52 "operator==", "operator!=", "operator<",
53 "operator>", "operator<=", "operator>="};
54
NonTrivialTypesLibcMemoryCallsCheck(StringRef Name,ClangTidyContext * Context)55 NonTrivialTypesLibcMemoryCallsCheck::NonTrivialTypesLibcMemoryCallsCheck(
56 StringRef Name, ClangTidyContext *Context)
57 : ClangTidyCheck(Name, Context),
58 MemSetNames(Options.get("MemSetNames", "")),
59 MemCpyNames(Options.get("MemCpyNames", "")),
60 MemCmpNames(Options.get("MemCmpNames", "")) {}
61
storeOptions(ClangTidyOptions::OptionMap & Opts)62 void NonTrivialTypesLibcMemoryCallsCheck::storeOptions(
63 ClangTidyOptions::OptionMap &Opts) {
64 Options.store(Opts, "MemSetNames", MemSetNames);
65 Options.store(Opts, "MemCpyNames", MemCpyNames);
66 Options.store(Opts, "MemCmpNames", MemCmpNames);
67 }
68
registerMatchers(MatchFinder * Finder)69 void NonTrivialTypesLibcMemoryCallsCheck::registerMatchers(
70 MatchFinder *Finder) {
71 using namespace ast_matchers::internal;
72 auto IsStructPointer = [](Matcher<CXXRecordDecl> Constraint = anything(),
73 bool Bind = false) {
74 return expr(unaryOperator(
75 hasOperatorName("&"),
76 hasUnaryOperand(declRefExpr(
77 allOf(hasType(cxxRecordDecl(Constraint)),
78 hasType(Bind ? qualType().bind("Record") : qualType()))))));
79 };
80 auto IsRecordSizeOf =
81 expr(sizeOfExpr(hasArgumentOfType(equalsBoundNode("Record"))));
82 auto ArgChecker = [&](Matcher<CXXRecordDecl> RecordConstraint,
83 BindableMatcher<Stmt> SecondArg = expr()) {
84 return allOf(argumentCountIs(3),
85 hasArgument(0, IsStructPointer(RecordConstraint, true)),
86 hasArgument(1, SecondArg), hasArgument(2, IsRecordSizeOf));
87 };
88
89 Finder->addMatcher(
90 callExpr(callee(namedDecl(hasAnyName(
91 utils::options::parseListPair(BuiltinMemSet, MemSetNames)))),
92 ArgChecker(unless(isTriviallyDefaultConstructible())))
93 .bind("lazyConstruct"),
94 this);
95 Finder->addMatcher(
96 callExpr(callee(namedDecl(hasAnyName(
97 utils::options::parseListPair(BuiltinMemCpy, MemCpyNames)))),
98 ArgChecker(unless(isTriviallyCopyable()), IsStructPointer()))
99 .bind("lazyCopy"),
100 this);
101 Finder->addMatcher(
102 callExpr(callee(namedDecl(hasAnyName(
103 utils::options::parseListPair(BuiltinMemCmp, MemCmpNames)))),
104 ArgChecker(hasMethod(hasAnyName(ComparisonOperators)),
105 IsStructPointer()))
106 .bind("lazyCompare"),
107 this);
108 }
109
check(const MatchFinder::MatchResult & Result)110 void NonTrivialTypesLibcMemoryCallsCheck::check(
111 const MatchFinder::MatchResult &Result) {
112 if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyConstruct")) {
113 diag(Caller->getBeginLoc(), "calling %0 on a non-trivially default "
114 "constructible class is undefined")
115 << cast<NamedDecl>(Caller->getCalleeDecl());
116 }
117 if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyCopy")) {
118 diag(Caller->getBeginLoc(),
119 "calling %0 on a non-trivially copyable class is undefined")
120 << cast<NamedDecl>(Caller->getCalleeDecl());
121 }
122 if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyCompare")) {
123 diag(Caller->getBeginLoc(),
124 "consider using comparison operators instead of calling %0")
125 << cast<NamedDecl>(Caller->getCalleeDecl());
126 }
127 }
128
129 } // namespace cert
130 } // namespace tidy
131 } // namespace clang
132