1 | //===--- UndefinedMemoryManipulationCheck.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 "UndefinedMemoryManipulationCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | |
13 | using namespace clang::ast_matchers; |
14 | |
15 | namespace clang::tidy::bugprone { |
16 | |
17 | namespace { |
18 | AST_MATCHER(CXXRecordDecl, isNotTriviallyCopyable) { |
19 | // For incomplete types, assume they are TriviallyCopyable. |
20 | return Node.hasDefinition() ? !Node.isTriviallyCopyable() : false; |
21 | } |
22 | } // namespace |
23 | |
24 | void UndefinedMemoryManipulationCheck::registerMatchers(MatchFinder *Finder) { |
25 | const auto HasNotTriviallyCopyableDecl = |
26 | hasDeclaration(InnerMatcher: cxxRecordDecl(isNotTriviallyCopyable())); |
27 | const auto ArrayOfNotTriviallyCopyable = |
28 | arrayType(hasElementType(HasNotTriviallyCopyableDecl)); |
29 | const auto NotTriviallyCopyableObject = hasType(InnerMatcher: hasCanonicalType( |
30 | InnerMatcher: anyOf(pointsTo(InnerMatcher: qualType(anyOf(HasNotTriviallyCopyableDecl, |
31 | ArrayOfNotTriviallyCopyable))), |
32 | ArrayOfNotTriviallyCopyable))); |
33 | |
34 | // Check whether destination object is not TriviallyCopyable. |
35 | // Applicable to all three memory manipulation functions. |
36 | Finder->addMatcher(NodeMatch: callExpr(callee(InnerMatcher: functionDecl(hasAnyName( |
37 | "::memset" , "::memcpy" , "::memmove" ))), |
38 | hasArgument(N: 0, InnerMatcher: NotTriviallyCopyableObject)) |
39 | .bind(ID: "dest" ), |
40 | Action: this); |
41 | |
42 | // Check whether source object is not TriviallyCopyable. |
43 | // Only applicable to memcpy() and memmove(). |
44 | Finder->addMatcher( |
45 | NodeMatch: callExpr(callee(InnerMatcher: functionDecl(hasAnyName("::memcpy" , "::memmove" ))), |
46 | hasArgument(N: 1, InnerMatcher: NotTriviallyCopyableObject)) |
47 | .bind(ID: "src" ), |
48 | Action: this); |
49 | } |
50 | |
51 | void UndefinedMemoryManipulationCheck::check( |
52 | const MatchFinder::MatchResult &Result) { |
53 | if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>(ID: "dest" )) { |
54 | QualType DestType = Call->getArg(Arg: 0)->IgnoreImplicit()->getType(); |
55 | if (!DestType->getPointeeType().isNull()) |
56 | DestType = DestType->getPointeeType(); |
57 | diag(Loc: Call->getBeginLoc(), Description: "undefined behavior, destination object type %0 " |
58 | "is not TriviallyCopyable" ) |
59 | << DestType; |
60 | } |
61 | if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>(ID: "src" )) { |
62 | QualType SourceType = Call->getArg(Arg: 1)->IgnoreImplicit()->getType(); |
63 | if (!SourceType->getPointeeType().isNull()) |
64 | SourceType = SourceType->getPointeeType(); |
65 | diag(Loc: Call->getBeginLoc(), |
66 | Description: "undefined behavior, source object type %0 is not TriviallyCopyable" ) |
67 | << SourceType; |
68 | } |
69 | } |
70 | |
71 | } // namespace clang::tidy::bugprone |
72 | |