1 //===--- SuspiciousMemoryComparisonCheck.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 "SuspiciousMemoryComparisonCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::bugprone
{
18 static std::optional
<uint64_t> tryEvaluateSizeExpr(const Expr
*SizeExpr
,
19 const ASTContext
&Ctx
) {
20 Expr::EvalResult Result
;
21 if (SizeExpr
->EvaluateAsRValue(Result
, Ctx
))
23 CharUnits::fromQuantity(Result
.Val
.getInt().getExtValue()));
27 void SuspiciousMemoryComparisonCheck::registerMatchers(MatchFinder
*Finder
) {
29 callExpr(callee(namedDecl(
30 anyOf(hasName("::memcmp"), hasName("::std::memcmp")))),
31 unless(isInstantiationDependent()))
36 void SuspiciousMemoryComparisonCheck::check(
37 const MatchFinder::MatchResult
&Result
) {
38 const ASTContext
&Ctx
= *Result
.Context
;
39 const auto *CE
= Result
.Nodes
.getNodeAs
<CallExpr
>("call");
41 const Expr
*SizeExpr
= CE
->getArg(2);
42 assert(SizeExpr
!= nullptr && "Third argument of memcmp is mandatory.");
43 std::optional
<uint64_t> ComparedBits
= tryEvaluateSizeExpr(SizeExpr
, Ctx
);
45 for (unsigned int ArgIndex
= 0; ArgIndex
< 2; ++ArgIndex
) {
46 const Expr
*ArgExpr
= CE
->getArg(ArgIndex
);
47 QualType ArgType
= ArgExpr
->IgnoreImplicit()->getType();
48 const Type
*PointeeType
= ArgType
->getPointeeOrArrayElementType();
49 assert(PointeeType
!= nullptr && "PointeeType should always be available.");
50 QualType
PointeeQualifiedType(PointeeType
, 0);
52 if (PointeeType
->isRecordType()) {
53 if (const RecordDecl
*RD
=
54 PointeeType
->getAsRecordDecl()->getDefinition()) {
55 if (const auto *CXXDecl
= dyn_cast
<CXXRecordDecl
>(RD
)) {
56 if (!CXXDecl
->isStandardLayout()) {
57 diag(CE
->getBeginLoc(),
58 "comparing object representation of non-standard-layout type "
59 "%0; consider using a comparison operator instead")
60 << PointeeQualifiedType
;
67 if (!PointeeType
->isIncompleteType()) {
68 uint64_t PointeeSize
= Ctx
.getTypeSize(PointeeType
);
69 if (ComparedBits
&& *ComparedBits
>= PointeeSize
&&
70 !Ctx
.hasUniqueObjectRepresentations(PointeeQualifiedType
)) {
71 diag(CE
->getBeginLoc(),
72 "comparing object representation of type %0 which does not have a "
73 "unique object representation; consider comparing %select{the "
74 "values|the members of the object}1 manually")
75 << PointeeQualifiedType
<< (PointeeType
->isRecordType() ? 1 : 0);
82 } // namespace clang::tidy::bugprone