diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -49,6 +49,7 @@ #include "StringLiteralWithEmbeddedNulCheck.h" #include "SuspiciousEnumUsageCheck.h" #include "SuspiciousIncludeCheck.h" +#include "SuspiciousMemoryComparisonCheck.h" #include "SuspiciousMemsetUsageCheck.h" #include "SuspiciousMissingCommaCheck.h" #include "SuspiciousSemicolonCheck.h" @@ -151,6 +152,8 @@ "bugprone-suspicious-enum-usage"); CheckFactories.registerCheck( "bugprone-suspicious-include"); + CheckFactories.registerCheck( + "bugprone-suspicious-memory-comparison"); CheckFactories.registerCheck( "bugprone-suspicious-memset-usage"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -44,6 +44,7 @@ StringLiteralWithEmbeddedNulCheck.cpp SuspiciousEnumUsageCheck.cpp SuspiciousIncludeCheck.cpp + SuspiciousMemoryComparisonCheck.cpp SuspiciousMemsetUsageCheck.cpp SuspiciousMissingCommaCheck.cpp SuspiciousSemicolonCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousMemoryComparisonCheck.h b/clang-tools-extra/clang-tidy/bugprone/SuspiciousMemoryComparisonCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousMemoryComparisonCheck.h @@ -0,0 +1,35 @@ +//===--- SuspiciousMemoryComparisonCheck.h - clang-tidy ---------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSMEMORYCOMPARISONCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSMEMORYCOMPARISONCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Finds potentially incorrect calls to ``memcmp()`` based on properties of the +/// arguments. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-memory-comparison.html +class SuspiciousMemoryComparisonCheck : public ClangTidyCheck { +public: + SuspiciousMemoryComparisonCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSMEMORYCOMPARISONCHECK_H diff --git a/clang-tools-extra/clang-tidy/bugprone/SuspiciousMemoryComparisonCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SuspiciousMemoryComparisonCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SuspiciousMemoryComparisonCheck.cpp @@ -0,0 +1,84 @@ +//===--- SuspiciousMemoryComparisonCheck.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 "SuspiciousMemoryComparisonCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bugprone { + +static llvm::Optional tryEvaluateSizeExpr(const Expr *SizeExpr, + const ASTContext &Ctx) { + Expr::EvalResult Result; + if (SizeExpr->EvaluateAsRValue(Result, Ctx)) + return Ctx.toBits( + CharUnits::fromQuantity(Result.Val.getInt().getExtValue())); + return None; +} + +void SuspiciousMemoryComparisonCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(callee(namedDecl( + anyOf(hasName("::memcmp"), hasName("::std::memcmp"))))) + .bind("call"), + this); +} + +void SuspiciousMemoryComparisonCheck::check( + const MatchFinder::MatchResult &Result) { + const ASTContext &Ctx = *Result.Context; + const auto *CE = Result.Nodes.getNodeAs("call"); + + const Expr *SizeExpr = CE->getArg(2); + assert(SizeExpr != nullptr && "Third argument of memcmp is mandatory."); + llvm::Optional ComparedBits = tryEvaluateSizeExpr(SizeExpr, Ctx); + + for (unsigned int i = 0; i < 2; ++i) { + const Expr *ArgExpr = CE->getArg(i); + QualType ArgType = ArgExpr->IgnoreImplicit()->getType(); + const Type *PointeeType = ArgType->getPointeeOrArrayElementType(); + assert(PointeeType != nullptr && "PointeeType should always be available."); + QualType PointeeQualifiedType(PointeeType, 0); + + if (PointeeType->isRecordType()) { + if (const RecordDecl *RD = + PointeeType->getAsRecordDecl()->getDefinition()) { + if (const auto *CXXDecl = dyn_cast(RD)) { + if (!CXXDecl->isStandardLayout()) { + diag(CE->getBeginLoc(), + "comparing object representation of non-standard-layout type " + "%0; consider using a comparison operator instead") + << PointeeQualifiedType; + break; + } + } + } + } + + if (!PointeeType->isIncompleteType()) { + uint64_t PointeeSize = Ctx.getTypeSize(PointeeType); + if (ComparedBits.hasValue() && *ComparedBits >= PointeeSize && + !Ctx.hasUniqueObjectRepresentations(PointeeQualifiedType)) { + diag(CE->getBeginLoc(), + "comparing object representation of type %0 which does not have " + "unique object representations; consider comparing the values " + "manually") + << PointeeQualifiedType; + break; + } + } + } +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -13,6 +13,7 @@ #include "../bugprone/ReservedIdentifierCheck.h" #include "../bugprone/SignedCharMisuseCheck.h" #include "../bugprone/SpuriouslyWakeUpFunctionsCheck.h" +#include "../bugprone/SuspiciousMemoryComparisonCheck.h" #include "../bugprone/UnhandledSelfAssignmentCheck.h" #include "../google/UnnamedNamespaceInHeaderCheck.h" #include "../misc/NewDeleteOverloadsCheck.h" @@ -96,8 +97,13 @@ "cert-dcl37-c"); // ENV CheckFactories.registerCheck("cert-env33-c"); + // EXP + CheckFactories.registerCheck( + "cert-exp42-c"); // FLP CheckFactories.registerCheck("cert-flp30-c"); + CheckFactories.registerCheck( + "cert-flp37-c"); // FIO CheckFactories.registerCheck("cert-fio38-c"); // ERR diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -91,6 +91,11 @@ Finds structs that are inefficiently packed or aligned, and recommends packing and/or aligning of said structs as needed. +- New :doc:`bugprone-suspicious-memory-comparison + ` check. + + Finds potentially incorrect calls to ``memcmp()`` based on properties of the arguments. + - New :doc:`cppcoreguidelines-prefer-member-initializer ` check. @@ -111,6 +116,16 @@ Flags functions with Cognitive Complexity metric exceeding the configured limit. +- New alias :doc:`cert-exp42-c + ` to + :doc:`bugprone-suspicious-memory-comparison + ` was added. + +- New alias :doc:`cert-flp37-c + ` to + :doc:`bugprone-suspicious-memory-comparison + ` was added. + Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-suspicious-memory-comparison.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-suspicious-memory-comparison.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-suspicious-memory-comparison.rst @@ -0,0 +1,26 @@ +.. title:: clang-tidy - bugprone-suspicious-memory-comparison + +bugprone-suspicious-memory-comparison +===================================== + +Finds potentially incorrect calls to ``memcmp()`` based on properties of the arguments. The following cases are covered: + +**Case 1: Non-standard-layout type** + +Comparing the object representaions of non-standard-layout objects may not properly compare the value representations. + +See also: +`OOP57-CPP. Prefer special member functions and overloaded operators to C Standard Library functions +`_ + +**Case 2: Type with no unique object representations** + +Objects with the same value may not have the same object representaions. +This may be caused by padding or floating-point types. + +See also: +`EXP42-C. Do not compare padding data +`_ +and +`FLP37-C. Do not use object representations to compare floating-point values +`_ diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert-exp42-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-exp42-c.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert-exp42-c.rst @@ -0,0 +1,8 @@ +.. meta:: + :http-equiv=refresh: 5;URL=bugprone-suspicious-memory-comparison.html + +cert-exp42-c +============ + +The cert-exp42-c check is an alias, please see +`bugprone-suspicious-memory-comparison `_ for more information. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert-flp37-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-flp37-c.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert-flp37-c.rst @@ -0,0 +1,8 @@ +.. meta:: + :http-equiv=refresh: 5;URL=bugprone-suspicious-memory-comparison.html + +cert-flp37-c +============ + +The cert-flp37-c check is an alias, please see +`bugprone-suspicious-memory-comparison `_ for more information. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -86,6 +86,7 @@ `bugprone-string-literal-with-embedded-nul `_, `bugprone-suspicious-enum-usage `_, `bugprone-suspicious-include `_, + `bugprone-suspicious-memory-comparison `_, `bugprone-suspicious-memset-usage `_, "Yes" `bugprone-suspicious-missing-comma `_, `bugprone-suspicious-semicolon `_, "Yes" @@ -109,7 +110,9 @@ `cert-err52-cpp `_, `cert-err58-cpp `_, `cert-err60-cpp `_, + `cert-exp42-c `_, `cert-flp30-c `_, + `cert-flp37-c `_, `cert-mem57-cpp `_, `cert-msc50-cpp `_, `cert-msc51-cpp `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison-32bits.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison-32bits.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison-32bits.cpp @@ -0,0 +1,35 @@ +// RUN: %check_clang_tidy %s bugprone-suspicious-memory-comparison %t \ +// RUN: -- -- -target x86_64-unknown-unknown -m32 + +static_assert(sizeof(int *) == sizeof(int)); + +namespace std { +typedef __SIZE_TYPE__ size_t; +int memcmp(const void *lhs, const void *rhs, size_t count); +} // namespace std + +namespace no_padding_on_32bit { +struct S { + int x; + int *y; +}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); +} +} // namespace no_padding_on_32bit + +namespace inner_padding { +struct S { + char x; + int y; +}; +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'inner_padding::S' which does not have unique object representations; + // consider comparing the values manually +} +} // namespace inner_padding diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison.c new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison.c @@ -0,0 +1,275 @@ +// RUN: %check_clang_tidy %s bugprone-suspicious-memory-comparison %t \ +// RUN: -- -- -target x86_64-unknown-unknown -std=c99 + +typedef __SIZE_TYPE__ size_t; +int memcmp(const void *lhs, const void *rhs, size_t count); + +// Examples from cert rule exp42-c + +struct S { + char c; + int i; + char buffer[13]; +}; + +void exp42_c_noncompliant(const struct S *left, const struct S *right) { + if ((left && right) && (0 == memcmp(left, right, sizeof(struct S)))) { + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: comparing object representation + // of type 'struct S' which does not have unique object representations; + // consider comparing the values manually + } +} + +void exp42_c_compliant(const struct S *left, const struct S *right) { + if ((left && right) && (left->c == right->c) && (left->i == right->i) && + (0 == memcmp(left->buffer, right->buffer, 13))) { + } +} + +#pragma pack(push, 1) +struct Packed_S { + char c; + int i; + char buffer[13]; +}; +#pragma pack(pop) + +void compliant_packed(const struct Packed_S *left, + const struct Packed_S *right) { + if ((left && right) && (0 == memcmp(left, right, sizeof(struct Packed_S)))) { + // no-warning + } +} + +// Examples from cert rule flp37-c + +struct S2 { + int i; + float f; +}; + +int flp37_c_noncompliant(const struct S2 *s1, const struct S2 *s2) { + if (!s1 && !s2) + return 1; + else if (!s1 || !s2) + return 0; + return 0 == memcmp(s1, s2, sizeof(struct S2)); + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: comparing object representation + // of type 'struct S2' which does not have unique object representations; + // consider comparing the values manually +} + +int flp37_c_compliant(const struct S2 *s1, const struct S2 *s2) { + if (!s1 && !s2) + return 1; + else if (!s1 || !s2) + return 0; + return s1->i == s2->i && s1->f == s2->f; + // no-warning +} + +void Test_Float() { + float a, b; + memcmp(&a, &b, sizeof(float)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation + // of type 'float' which does not have unique object representations; consider + // comparing the values manually +} + +void TestArray_Float() { + float a[3], b[3]; + memcmp(a, b, sizeof(a)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation + // of type 'float' which does not have unique object representations; consider + // comparing the values manually +} + +struct PredeclaredType; + +void Test_PredeclaredType(const struct PredeclaredType *lhs, + const struct PredeclaredType *rhs) { + memcmp(lhs, rhs, 1); // no-warning: predeclared type +} + +struct NoPadding { + int x; + int y; +}; + +void Test_NoPadding() { + struct NoPadding a, b; + memcmp(&a, &b, sizeof(struct NoPadding)); +} + +void TestArray_NoPadding() { + struct NoPadding a[3], b[3]; + memcmp(a, b, 3 * sizeof(struct NoPadding)); +} + +struct TrailingPadding { + int i; + char c; +}; + +void Test_TrailingPadding() { + struct TrailingPadding a, b; + memcmp(&a, &b, sizeof(struct TrailingPadding)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct TrailingPadding' which does not have unique object + // representations; consider comparing the values manually + memcmp(&a, &b, sizeof(int)); // no-warning: not comparing entire object + memcmp(&a, &b, 2 * sizeof(int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct TrailingPadding' which does not have unique object + // representations; consider comparing the values manually +} + +void Test_UnknownCount(size_t count) { + struct TrailingPadding a, b; + memcmp(&a, &b, count); // no-warning: unknown count value +} + +void Test_ExplicitVoidCast() { + struct TrailingPadding a, b; + memcmp((void *)&a, (void *)&b, + sizeof(struct TrailingPadding)); // no-warning: explicit cast +} + +void TestArray_TrailingPadding() { + struct TrailingPadding a[3], b[3]; + memcmp(a, b, 3 * sizeof(struct TrailingPadding)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct TrailingPadding' which does not have unique object + // representations; consider comparing the values manually +} + +struct InnerPadding { + char c; + int i; +}; + +void Test_InnerPadding() { + struct InnerPadding a, b; + memcmp(&a, &b, sizeof(struct InnerPadding)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct InnerPadding' which does not have unique object + // representations; consider comparing the values manually +} + +struct Bitfield_TrailingPaddingBytes { + int x : 10; + int y : 6; +}; + +void Test_Bitfield_TrailingPaddingBytes() { + struct Bitfield_TrailingPaddingBytes a, b; + memcmp(&a, &b, sizeof(struct S)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct Bitfield_TrailingPaddingBytes' which does not have unique + // object representations; consider comparing the values manually +} + +struct Bitfield_TrailingPaddingBits { + int x : 10; + int y : 20; +}; + +void Test_Bitfield_TrailingPaddingBits() { + struct Bitfield_TrailingPaddingBits a, b; + memcmp(&a, &b, sizeof(struct Bitfield_TrailingPaddingBits)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct Bitfield_TrailingPaddingBits' which does not have unique + // object representations; consider comparing the values manually +} + +struct Bitfield_InnerPaddingBits { + char x : 2; + int : 0; + char y : 8; +}; + +void Test_Bitfield_InnerPaddingBits() { + struct Bitfield_InnerPaddingBits a, b; + memcmp(&a, &b, sizeof(struct Bitfield_InnerPaddingBits)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct Test_Bitfield_InnerPaddingBits' which does not have unique + // object representations; consider comparing the values manually +} + +struct PaddingAfterUnion { + union { + unsigned short a; + short b; + } x; + + int y; +}; + +void Test_PaddingAfterUnion() { + struct PaddingAfterUnion a, b; + memcmp(&a, &b, sizeof(struct PaddingAfterUnion)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct PaddingAfterUnion' which does not have unique object + // representations; consider comparing the values manually +} + +struct Union_NoPadding { + union { + int a; + unsigned int b; + } x; + + int y; +}; + +void Test_Union_NoPadding() { + struct Union_NoPadding a, b; + memcmp(&a, &b, 2 * sizeof(int)); + memcmp(&a, &b, sizeof(struct Union_NoPadding)); +} + +union UnionWithPaddingInNestedStruct { + int i; + + struct { + int i; + char c; + } x; +}; + +void Test_UnionWithPaddingInNestedStruct() { + union UnionWithPaddingInNestedStruct a, b; + memcmp(&a, &b, sizeof(union UnionWithPaddingInNestedStruct)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'union UnionWithPaddingInNestedStruct' which does not have unique + // object representations; consider comparing the values manually +} + +struct PaddingInNested { + struct TrailingPadding x; + char y; +}; + +void Test_PaddingInNested() { + struct PaddingInNested a, b; + memcmp(&a, &b, sizeof(struct PaddingInNested)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct PaddingInNested' which does not have unique object + // representations; consider comparing the values manually +} + +struct PaddingAfterNested { + struct { + char a; + char b; + } x; + int y; +}; + +void Test_PaddingAfterNested() { + struct PaddingAfterNested a, b; + memcmp(&a, &b, sizeof(struct PaddingAfterNested)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'struct PaddingAfterNested' which does not have unique object + // representations; consider comparing the values manually +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-suspicious-memory-comparison.cpp @@ -0,0 +1,230 @@ +// RUN: %check_clang_tidy %s bugprone-suspicious-memory-comparison %t \ +// RUN: -- -- -target x86_64-unknown-unknown + +namespace std { +typedef __SIZE_TYPE__ size_t; +int memcmp(const void *lhs, const void *rhs, size_t count); +} // namespace std + +namespace sei_cert_example_oop57_cpp { +class C { + int i; + +public: + virtual void f(); +}; + +void f(C &c1, C &c2) { + if (!std::memcmp(&c1, &c2, sizeof(C))) { + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: comparing object representation + // of non-standard-layout type 'sei_cert_example_oop57_cpp::C'; consider + // using a comparison operator instead + } +} +} // namespace sei_cert_example_oop57_cpp + +namespace inner_padding_64bit_only { +struct S { + int x; + int *y; +}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'inner_padding_64bit_only::S' which does not have unique object + // representations; consider comparing the values manually +} +} // namespace inner_padding_64bit_only + +namespace padding_in_base { +class Base { + char c; + int i; +}; + +class Derived : public Base {}; + +class Derived2 : public Derived {}; + +void testDerived() { + Derived a, b; + std::memcmp(&a, &b, sizeof(Base)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'padding_in_base::Derived' which does not have unique object + // representations; consider comparing the values manually + std::memcmp(&a, &b, sizeof(Derived)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'padding_in_base::Derived' which does not have unique object + // representations; consider comparing the values manually +} + +void testDerived2() { + Derived2 a, b; + std::memcmp(&a, &b, sizeof(Base)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'padding_in_base::Derived2' which does not have unique object + // representations; consider comparing the values manually + std::memcmp(&a, &b, sizeof(Derived2)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'padding_in_base::Derived2' which does not have unique object + // representations; consider comparing the values manually +} + +} // namespace padding_in_base + +namespace no_padding_in_base { +class Base { + int a, b; +}; + +class Derived : public Base {}; + +class Derived2 : public Derived {}; + +void testDerived() { + Derived a, b; + std::memcmp(&a, &b, sizeof(Base)); + std::memcmp(&a, &b, sizeof(Derived)); +} + +void testDerived2() { + Derived2 a, b; + std::memcmp(&a, &b, sizeof(char)); + std::memcmp(&a, &b, sizeof(Base)); + std::memcmp(&a, &b, sizeof(Derived2)); +} +} // namespace no_padding_in_base + +namespace non_standard_layout { +class C { +private: + int x; + +public: + int y; +}; + +void test() { + C a, b; + std::memcmp(&a, &b, sizeof(C)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation + // of non-standard-layout type 'non_standard_layout::C'; consider using a + // comparison operator instead +} + +} // namespace non_standard_layout + +namespace static_ignored { +struct S { + static char c; + int i; +}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); +} +} // namespace static_ignored + +namespace operator_void_ptr { +struct S { + operator void *() const; +}; + +void test() { + S s; + std::memcmp(s, s, sizeof(s)); +} +} // namespace operator_void_ptr + +namespace empty_struct { +struct S {}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'empty_struct::S' which does not have unique object representations; + // consider comparing the values manually +} +} // namespace empty_struct + +namespace empty_field { +struct Empty {}; +struct S { + Empty e; +}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'empty_field::S' which does not have unique object representations; + // consider comparing the values manually +} +} // namespace empty_field + +namespace no_unique_address_attribute { +struct Empty {}; + +namespace no_padding { +struct S { + char c; + [[no_unique_address]] Empty e; +}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); +} + +} // namespace no_padding + +namespace multiple_empties_same_type { +struct S { + char c; + [[no_unique_address]] Empty e1, e2; +}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'multiple_empties_same_type::S' which does not have unique object + // representations; consider comparing the values manually +} + +} // namespace multiple_empties_same_type + +namespace multiple_empties_different_types { +struct Empty2 {}; + +struct S { + char c; + [[no_unique_address]] Empty e1; + [[no_unique_address]] Empty2 e2; +}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); +} +} // namespace multiple_empties_different_types +} // namespace no_unique_address_attribute + +namespace alignment { +struct S { + char x; + alignas(sizeof(int)) char y[sizeof(int)]; +}; + +void test() { + S a, b; + std::memcmp(&a, &b, sizeof(S)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comparing object representation of + // type 'alignment::S' which does not have unique object representations; + // consider comparing the values manually +} +} // namespace alignment