https://github.com/unterumarmung created https://github.com/llvm/llvm-project/pull/189962
Fixes #189499 Assisted by Codex. >From 5de5f10c03fe2a05ae7cbcdca4a498090015a082 Mon Sep 17 00:00:00 2001 From: Daniil Dudkin <[email protected]> Date: Wed, 1 Apr 2026 16:37:47 +0300 Subject: [PATCH] [clang-tidy] Add `modernize-use-bit-cast` check --- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/ModernizeTidyModule.cpp | 2 + .../clang-tidy/modernize/UseBitCastCheck.cpp | 308 ++++++++++++++++++ .../clang-tidy/modernize/UseBitCastCheck.h | 44 +++ clang-tools-extra/docs/ReleaseNotes.rst | 6 + .../docs/clang-tidy/checks/list.rst | 1 + .../checks/modernize/use-bit-cast.rst | 62 ++++ .../checkers/modernize/use-bit-cast.cpp | 291 +++++++++++++++++ 8 files changed, 715 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-bit-cast.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 2c5c44db587fe..728a0b21613fd 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -32,6 +32,7 @@ add_clang_library(clangTidyModernizeModule STATIC TypeTraitsCheck.cpp UnaryStaticAssertCheck.cpp UseAutoCheck.cpp + UseBitCastCheck.cpp UseBoolLiteralsCheck.cpp UseConstraintsCheck.cpp UseDefaultMemberInitCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index cc13da7535bcb..6e823a558c299 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -32,6 +32,7 @@ #include "TypeTraitsCheck.h" #include "UnaryStaticAssertCheck.h" #include "UseAutoCheck.h" +#include "UseBitCastCheck.h" #include "UseBoolLiteralsCheck.h" #include "UseConstraintsCheck.h" #include "UseDefaultMemberInitCheck.h" @@ -88,6 +89,7 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck<MinMaxUseInitializerListCheck>( "modernize-min-max-use-initializer-list"); CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value"); + CheckFactories.registerCheck<UseBitCastCheck>("modernize-use-bit-cast"); CheckFactories.registerCheck<UseDesignatedInitializersCheck>( "modernize-use-designated-initializers"); CheckFactories.registerCheck<UseIntegerSignComparisonCheck>( diff --git a/clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.cpp new file mode 100644 index 0000000000000..bc91f9fee1775 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.cpp @@ -0,0 +1,308 @@ +//===----------------------------------------------------------------------===// +// +// 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 "UseBitCastCheck.h" +#include "../utils/Matchers.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +static const Expr *stripMemcpyArgument(const Expr *ExprNode) { + ExprNode = ExprNode->IgnoreParenImpCasts(); + while (const auto *Cast = dyn_cast<ExplicitCastExpr>(ExprNode)) + ExprNode = Cast->getSubExpr()->IgnoreParenImpCasts(); + return ExprNode; +} + +static bool isSupportedMemcpyObjectExpr(const Expr *ExprNode) { + ExprNode = ExprNode->IgnoreParenImpCasts(); + + if (isa<DeclRefExpr>(ExprNode)) + return true; + + const auto *Member = dyn_cast<MemberExpr>(ExprNode); + if (!Member || !isa<FieldDecl>(Member->getMemberDecl())) + if (const auto *MemberPointer = dyn_cast<BinaryOperator>(ExprNode)) + if (MemberPointer->getOpcode() == BO_PtrMemD || + MemberPointer->getOpcode() == BO_PtrMemI) + return isSupportedMemcpyObjectExpr(MemberPointer->getLHS()); + + if (!Member) + return false; + + return isSupportedMemcpyObjectExpr(Member->getBase()); +} + +static const Expr *extractMemcpyObjectExpr(const Expr *ExprNode) { + ExprNode = stripMemcpyArgument(ExprNode); + const auto *AddressOf = dyn_cast<UnaryOperator>(ExprNode); + if (!AddressOf || AddressOf->getOpcode() != UO_AddrOf) + return nullptr; + + const Expr *ObjectExpr = AddressOf->getSubExpr()->IgnoreParenImpCasts(); + return isSupportedMemcpyObjectExpr(ObjectExpr) ? ObjectExpr : nullptr; +} + +static bool isSupportedMemcpyArgType(QualType Type, const ASTContext &Context, + bool RequireMutable) { + if (Type.isNull()) + return false; + + const QualType CanonicalType = Type.getCanonicalType().getNonReferenceType(); + if (CanonicalType.isNull() || CanonicalType->isDependentType() || + CanonicalType->isIncompleteType() || + CanonicalType.isVolatileQualified() || + CanonicalType->isAnyPointerType() || CanonicalType->isArrayType() || + CanonicalType->isFunctionType()) + return false; + + if (RequireMutable) { + if (CanonicalType.isConstQualified()) + return false; + + if (const auto *Record = CanonicalType->getAsCXXRecordDecl()) + if (!Record->hasSimpleCopyAssignment() && + !Record->hasSimpleMoveAssignment()) + return false; + } + + return Type.getNonReferenceType().isTriviallyCopyableType(Context); +} + +static bool isSameUnqualifiedCanonicalType(QualType LHS, QualType RHS) { + return LHS.getCanonicalType().getUnqualifiedType() == + RHS.getCanonicalType().getUnqualifiedType(); +} + +static bool isMatchingSizeOfExpression(const Expr *SizeExpr, QualType SrcType, + QualType DstType, + const ASTContext &Context) { + const auto *UnaryExpr = + dyn_cast<UnaryExprOrTypeTraitExpr>(SizeExpr->IgnoreParenImpCasts()); + if (!UnaryExpr || UnaryExpr->getKind() != UETT_SizeOf || + SizeExpr->getBeginLoc().isMacroID()) + return false; + + const QualType SizeType = UnaryExpr->getTypeOfArgument(); + if (SizeType.isNull()) + return false; + + const QualType SizeCanonical = + SizeType.getCanonicalType().getUnqualifiedType(); + const QualType SrcCanonical = SrcType.getCanonicalType().getUnqualifiedType(); + const QualType DstCanonical = DstType.getCanonicalType().getUnqualifiedType(); + if (SizeCanonical != SrcCanonical && SizeCanonical != DstCanonical) + return false; + + return Context.getTypeSizeInChars(SrcCanonical) == + Context.getTypeSizeInChars(DstCanonical); +} + +static bool isStatementBody(const Stmt *Current, const Stmt *Parent) { + if (const auto *Block = dyn_cast<CompoundStmt>(Parent)) + return llvm::is_contained(Block->body(), Current); + + if (const auto *If = dyn_cast<IfStmt>(Parent)) + return If->getThen() == Current || If->getElse() == Current; + if (const auto *While = dyn_cast<WhileStmt>(Parent)) + return While->getBody() == Current; + if (const auto *Do = dyn_cast<DoStmt>(Parent)) + return Do->getBody() == Current; + if (const auto *For = dyn_cast<ForStmt>(Parent)) + return For->getBody() == Current; + if (const auto *RangeFor = dyn_cast<CXXForRangeStmt>(Parent)) + return RangeFor->getBody() == Current; + if (const auto *Label = dyn_cast<LabelStmt>(Parent)) + return Label->getSubStmt() == Current; + if (const auto *Case = dyn_cast<SwitchCase>(Parent)) + return Case->getSubStmt() == Current; + if (const auto *Attributed = dyn_cast<AttributedStmt>(Parent)) + return Attributed->getSubStmt() == Current; + + return false; +} + +namespace { + +// These states describe how to spell the replacement when only the memcpy call +// is replaced. An existing `(void)` cast is preserved by parenthesizing the +// assignment, while comma/discarded subexpressions need an injected `(void)`. +enum class MemcpyReplacementForm { + None, + StatementBody, + PreserveOuterVoidCast, + InjectVoidCast, +}; + +} // namespace + +static MemcpyReplacementForm getMemcpyReplacementForm(const Expr *ExprNode, + ASTContext &Context) { + const Stmt *Current = ExprNode; + MemcpyReplacementForm Kind = MemcpyReplacementForm::StatementBody; + + while (true) { + auto Parents = Context.getParents(*Current); + if (Parents.size() != 1) + return MemcpyReplacementForm::None; + + if (const auto *ParentExpr = Parents[0].get<Expr>()) { + if (isa<ExprWithCleanups, ImplicitCastExpr, MaterializeTemporaryExpr, + CXXBindTemporaryExpr, ParenExpr>(ParentExpr)) { + Current = ParentExpr; + continue; + } + + if (const auto *Cast = dyn_cast<CastExpr>(ParentExpr)) + if (Cast->getCastKind() == CK_ToVoid) + return MemcpyReplacementForm::PreserveOuterVoidCast; + + if (const auto *Comma = dyn_cast<BinaryOperator>(ParentExpr)) { + if (Comma->getOpcode() != BO_Comma) + return MemcpyReplacementForm::None; + if (Comma->getLHS() == Current) + return MemcpyReplacementForm::InjectVoidCast; + if (Comma->getRHS() == Current) { + Current = Comma; + Kind = MemcpyReplacementForm::InjectVoidCast; + continue; + } + } + + return MemcpyReplacementForm::None; + } + + const auto *ParentStmt = Parents[0].get<Stmt>(); + if (!ParentStmt || !isStatementBody(Current, ParentStmt)) + return MemcpyReplacementForm::None; + return Kind; + } +} + +namespace { + +AST_MATCHER(CallExpr, isDiscardedValueContext) { + return getMemcpyReplacementForm(&Node, Finder->getASTContext()) != + MemcpyReplacementForm::None; +} + +AST_MATCHER(CallExpr, isBitCastMemcpyCandidate) { + if (Node.getNumArgs() != 3 || Node.getBeginLoc().isMacroID()) + return false; + + const auto *DstExpr = extractMemcpyObjectExpr(Node.getArg(0)); + const auto *SrcExpr = extractMemcpyObjectExpr(Node.getArg(1)); + if (!DstExpr || !SrcExpr || DstExpr->getBeginLoc().isMacroID() || + SrcExpr->getBeginLoc().isMacroID()) + return false; + + const auto &Context = Finder->getASTContext(); + const QualType DstType = DstExpr->getType().getNonReferenceType(); + const QualType SrcType = SrcExpr->getType().getNonReferenceType(); + + return isSupportedMemcpyArgType(DstType, Context, /*RequireMutable=*/true) && + isSupportedMemcpyArgType(SrcType, Context, + /*RequireMutable=*/false) && + !isSameUnqualifiedCanonicalType(SrcType, DstType) && + isMatchingSizeOfExpression(Node.getArg(2), SrcType, DstType, Context); +} + +} // namespace + +static StringRef getSourceText(const Expr *ExprNode, const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getSourceText( + CharSourceRange::getTokenRange(ExprNode->getSourceRange()), SM, LangOpts); +} + +UseBitCastCheck::UseBitCastCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()) {} + +void UseBitCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); +} + +void UseBitCastCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void UseBitCastCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(callee(functionDecl(hasName("::memcpy"))), + isDiscardedValueContext(), unless(isInTemplateInstantiation()), + unless(hasAncestor(expr(matchers::hasUnevaluatedContext()))), + isBitCastMemcpyCandidate()) + .bind("memcpy"), + this); +} + +void UseBitCastCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MemcpyCall = Result.Nodes.getNodeAs<CallExpr>("memcpy"); + if (!MemcpyCall) + return; + + const auto *DstExpr = extractMemcpyObjectExpr(MemcpyCall->getArg(0)); + const auto *SrcExpr = extractMemcpyObjectExpr(MemcpyCall->getArg(1)); + if (!DstExpr || !SrcExpr) + return; + + const SourceManager &SM = *Result.SourceManager; + const LangOptions &LangOpts = getLangOpts(); + StringRef DstText = getSourceText(DstExpr, SM, LangOpts); + StringRef SrcText = getSourceText(SrcExpr, SM, LangOpts); + if (DstText.empty() || SrcText.empty()) + return; + + const MemcpyReplacementForm ReplacementForm = + getMemcpyReplacementForm(MemcpyCall, *Result.Context); + if (ReplacementForm == MemcpyReplacementForm::None) + return; + + const PrintingPolicy Policy(LangOpts); + const QualType DstType = + DstExpr->getType().getNonReferenceType().getUnqualifiedType(); + const std::string Assignment = std::string(DstText) + " = std::bit_cast<" + + DstType.getAsString(Policy) + ">(" + + std::string(SrcText) + ")"; + std::string Replacement = Assignment; + switch (ReplacementForm) { + case MemcpyReplacementForm::StatementBody: + break; + case MemcpyReplacementForm::PreserveOuterVoidCast: + Replacement = "(" + Assignment + ")"; + break; + case MemcpyReplacementForm::InjectVoidCast: + Replacement = "(void)(" + Assignment + ")"; + break; + case MemcpyReplacementForm::None: + return; + } + + const DiagnosticBuilder Diag = + diag(MemcpyCall->getBeginLoc(), + "use 'std::bit_cast' instead of 'memcpy' for type punning"); + Diag << FixItHint::CreateReplacement(MemcpyCall->getSourceRange(), + Replacement); + Diag << IncludeInserter.createIncludeInsertion( + SM.getFileID(MemcpyCall->getBeginLoc()), "<bit>"); +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.h b/clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.h new file mode 100644 index 0000000000000..c4672a7321d36 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.h @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// 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_MODERNIZE_USEBITCASTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEBITCASTCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" + +namespace clang::tidy::modernize { + +/// Finds conservative object-to-object ``memcpy`` type punning that can be +/// expressed as ``std::bit_cast``. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-bit-cast.html +class UseBitCastCheck : public ClangTidyCheck { +public: + UseBitCastCheck(StringRef Name, ClangTidyContext *Context); + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus20; + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + std::optional<TraversalKind> getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + utils::IncludeInserter IncludeInserter; +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEBITCASTCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 69dc5b9633398..0cf3c6fdfe6b8 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -139,6 +139,12 @@ New checks Finds common idioms which can be replaced by standard functions from the ``<bit>`` C++20 header. +- New :doc:`modernize-use-bit-cast + <clang-tidy/checks/modernize/use-bit-cast>` check. + + Finds conservative object-to-object ``memcpy`` type punning that can be + rewritten as ``std::bit_cast`` in C++20 and later. + - New :doc:`modernize-use-string-view <clang-tidy/checks/modernize/use-string-view>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 2b5be931271ec..6cb962d1c2705 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -311,6 +311,7 @@ Clang-Tidy Checks :doc:`modernize-type-traits <modernize/type-traits>`, "Yes" :doc:`modernize-unary-static-assert <modernize/unary-static-assert>`, "Yes" :doc:`modernize-use-auto <modernize/use-auto>`, "Yes" + :doc:`modernize-use-bit-cast <modernize/use-bit-cast>`, "Yes" :doc:`modernize-use-bool-literals <modernize/use-bool-literals>`, "Yes" :doc:`modernize-use-constraints <modernize/use-constraints>`, "Yes" :doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-bit-cast.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-bit-cast.rst new file mode 100644 index 0000000000000..03f78faf396d6 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-bit-cast.rst @@ -0,0 +1,62 @@ +.. title:: clang-tidy - modernize-use-bit-cast + +modernize-use-bit-cast +====================== + +Finds conservative object-to-object ``memcpy`` type punning that can be +rewritten as ``std::bit_cast`` in C++20 and later. + +The check targets the common pattern of copying the full object representation +of one trivially copyable object into another trivially copyable object of a +different type: + +.. code-block:: c++ + + float src = 1.0f; + unsigned int dst; + std::memcpy(&dst, &src, sizeof(src)); + +This is rewritten to: + +.. code-block:: c++ + + float src = 1.0f; + unsigned int dst; + dst = std::bit_cast<unsigned int>(src); + +The fix intentionally replaces only the ``memcpy`` call. It does not fold a +preceding declaration into ``auto dst = ...`` because doing so can change the +construction behavior of the destination object. + +It only matches direct named source and destination objects, or direct field +subobjects accessed through ``.``, ``->``, ``.*``, or ``->*``, and only when: + +* both object types are trivially copyable, +* neither object type is a pointer, array, function type, or volatile-qualified, +* the source and destination types differ, +* the copy size is expressed as ``sizeof(...)`` for either copied type, and +* the ``memcpy`` call appears in a discarded-value context, such as a statement + body, the operand of an explicit ``(void)`` cast, or a comma subexpression + whose value is discarded. + +The check intentionally does not diagnose: + +* pointer punning, +* array or buffer manipulation, +* macro expansions, +* dependent template cases, +* unevaluated contexts such as ``sizeof(memcpy(...))``, +* larger expressions where the ``memcpy`` value affects the enclosing + expression, such as conditions or operands of unrelated operators, +* calls where the return value of ``memcpy`` is used, or +* unrelated overloads such as a user-defined ``memcpy``. + +If needed, the fix also inserts ``#include <bit>``. + +Options +------- + +.. option:: IncludeStyle + + A string specifying which include-style is used, ``llvm`` or ``google``. + Default is ``llvm``. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast.cpp new file mode 100644 index 0000000000000..9820bf6e7987c --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast.cpp @@ -0,0 +1,291 @@ +// RUN: %check_clang_tidy -std=c++20-or-later %s modernize-use-bit-cast %t + +// CHECK-FIXES: #include <bit> + +void *memcpy(void *To, const void *From, unsigned long long Size); + +namespace std { +using ::memcpy; +} + +template <typename T> +struct identity { + using type = T; +}; + +struct NonTrivial { + NonTrivial(); + NonTrivial(const NonTrivial &); + int Value; +}; + +extern unsigned long long n; + +void basic_case() { + float src = 1.0f; + unsigned int dst; + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning [modernize-use-bit-cast] + // CHECK-FIXES: dst = std::bit_cast<unsigned int>(src); +} + +void unqualified_case() { + float src = 1.0f; + unsigned int dst; + memcpy(&dst, &src, sizeof(dst)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: dst = std::bit_cast<unsigned int>(src); +} + +void global_case() { + float src = 1.0f; + unsigned int dst; + ::memcpy(&dst, &src, sizeof(unsigned int)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: dst = std::bit_cast<unsigned int>(src); +} + +void explicit_cast_case() { + float src = 1.0f; + unsigned int dst = 0; + std::memcpy(static_cast<void *>(&dst), static_cast<const void *>(&src), + sizeof(dst)); + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: dst = std::bit_cast<unsigned int>(src); +} + +void alias_case() { + using U = identity<unsigned int>::type; + using F = identity<float>::type; + F src = 1.0f; + U dst; + std::memcpy(&dst, &src, sizeof(U)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: dst = std::bit_cast<U>(src); +} + +void const_source_case() { + const float src = 1.0f; + unsigned int dst; + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: dst = std::bit_cast<unsigned int>(src); +} + +void lambda_case() { + auto L = [] { + float src = 1.0f; + unsigned int dst; + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: dst = std::bit_cast<unsigned int>(src); + }; + L(); +} + +void if_body_case(bool Cond) { + float src = 1.0f; + unsigned int dst; + if (Cond) + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: if (Cond) + // CHECK-FIXES-NEXT: dst = std::bit_cast<unsigned int>(src); +} + +void comma_lhs_case() { + float src = 1.0f; + unsigned int dst; + int value = (std::memcpy(&dst, &src, sizeof(src)), 42); + (void)value; + // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: int value = ((void)(dst = std::bit_cast<unsigned int>(src)), 42); +} + +void void_cast_case() { + float src = 1.0f; + unsigned int dst; + (void)std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: (void)(dst = std::bit_cast<unsigned int>(src)); +} + +void same_type_case() { + float src = 1.0f; + float dst = 0.0f; + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void pointer_case(int *srcp) { + int *dstp; + std::memcpy(&dstp, &srcp, sizeof(srcp)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void array_case() { + unsigned char bytes[sizeof(float)]; + float src = 1.0f; + std::memcpy(bytes, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void buffer_pointer_case(float *srcp, unsigned int *dstp) { + std::memcpy(dstp, srcp, sizeof(*srcp)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void partial_copy_case() { + float src = 1.0f; + unsigned int dst; + std::memcpy(&dst, &src, 2); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void unknown_copy_case() { + float src = 1.0f; + unsigned int dst; + std::memcpy(&dst, &src, n); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void non_trivial_case(NonTrivial src) { + NonTrivial dst; + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void volatile_case() { + volatile float src = 1.0f; + unsigned int dst; + std::memcpy(&dst, const_cast<const float *>(&src), sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +struct Wrap { + float src; + unsigned int dst; +}; + +struct SourceStruct { + int Value; +}; + +struct DestStruct { + const int Value; +}; + +void member_case() { + Wrap W{1.0f, 0}; + std::memcpy(&W.dst, &W.src, sizeof(W.src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: W.dst = std::bit_cast<unsigned int>(W.src); +} + +void pointer_member_case(Wrap *P) { + std::memcpy(&P->dst, &P->src, sizeof(P->src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: P->dst = std::bit_cast<unsigned int>(P->src); +} + +void member_pointer_case(Wrap W, float Wrap::*Src, unsigned int Wrap::*Dst) { + std::memcpy(&(W.*Dst), &(W.*Src), sizeof(W.*Src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: W.*Dst = std::bit_cast<unsigned int>(W.*Src); +} + +void pointer_member_pointer_case(Wrap *P, float Wrap::*Src, + unsigned int Wrap::*Dst) { + std::memcpy(&(P->*Dst), &(P->*Src), sizeof(P->*Src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: P->*Dst = std::bit_cast<unsigned int>(P->*Src); +} + +void builtin_case() { + float src = 1.0f; + unsigned int dst; + __builtin_memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +namespace ns { +struct A { + unsigned int Value; +}; + +struct B { + unsigned int Value; +}; + +void memcpy(B *, const A *, unsigned long long); + +void overload_case() { + A src{0}; + B dst{0}; + memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} +} // namespace ns + +#define DO_COPY(Dst, Src) std::memcpy(&(Dst), &(Src), sizeof(Src)) + +void macro_case() { + float src = 1.0f; + unsigned int dst; + DO_COPY(dst, src); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +template <typename To, typename From> +requires(sizeof(To) == sizeof(From)) +To template_case(From src) { + To dst; + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + return dst; +} + +void unevaluated_case() { + float src = 1.0f; + unsigned int dst; + (void)sizeof(std::memcpy(&dst, &src, sizeof(src))); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:16: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void used_return_value_case() { + float src = 1.0f; + unsigned int dst; + void *Ptr = std::memcpy(&dst, &src, sizeof(src)); + (void)Ptr; + // CHECK-MESSAGES-NOT: :[[@LINE-2]]:15: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void comma_rhs_used_case() { + float src = 1.0f; + unsigned int dst; + void *Ptr = (0, std::memcpy(&dst, &src, sizeof(src))); + (void)Ptr; + // CHECK-MESSAGES-NOT: :[[@LINE-2]]:19: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void deleted_assignment_case(SourceStruct src) { + DestStruct dst{0}; + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void condition_use_case() { + float src = 1.0f; + unsigned int dst; + if (std::memcpy(&dst, &src, sizeof(src))) + (void)0; + // CHECK-MESSAGES-NOT: :[[@LINE-2]]:7: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void conditional_operand_case(bool Cond) { + float src = 1.0f; + unsigned int dst; + void *Ptr = nullptr; + (void)(Cond ? std::memcpy(&dst, &src, sizeof(src)) : Ptr); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:17: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
