https://github.com/unterumarmung updated https://github.com/llvm/llvm-project/pull/189962
>From 96418c587fb378dfc2a80fa268c645c3d94c6819 Mon Sep 17 00:00:00 2001 From: Daniil Dudkin <[email protected]> Date: Sat, 5 Sep 2026 13:28:11 +0300 Subject: [PATCH] Add modernize-use-bit-cast check --- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/ModernizeTidyModule.cpp | 2 + .../clang-tidy/modernize/UseBitCastCheck.cpp | 362 ++++++++++++ .../clang-tidy/modernize/UseBitCastCheck.h | 44 ++ clang-tools-extra/docs/ReleaseNotes.md | 6 + .../docs/clang-tidy/checks/list.md | 1 + .../checks/modernize/use-bit-cast.md | 47 ++ .../modernize/Inputs/use-bit-cast/header.h | 14 + .../modernize/use-bit-cast-header.cpp | 13 + .../modernize/use-bit-cast-overload.cpp | 22 + .../checkers/modernize/use-bit-cast.cpp | 538 ++++++++++++++++++ 11 files changed, 1050 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.md create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-bit-cast/header.h create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast-header.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast-overload.cpp 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 23aeb393ad016..d6b0440a7cd7f 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 62676469aefe7..5cc3963b5a296 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" @@ -120,6 +121,7 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck<UnaryStaticAssertCheck>( "modernize-unary-static-assert"); CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto"); + CheckFactories.registerCheck<UseBitCastCheck>("modernize-use-bit-cast"); CheckFactories.registerCheck<UseBoolLiteralsCheck>( "modernize-use-bool-literals"); CheckFactories.registerCheck<UseConstraintsCheck>( 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..f4b95569c2984 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseBitCastCheck.cpp @@ -0,0 +1,362 @@ +//===----------------------------------------------------------------------===// +// +// 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 "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/Tooling/FixIt.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/TypeSwitch.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +static bool isSupportedMemcpyObjectExpr(const Expr *ExprNode) { + ExprNode = ExprNode->IgnoreParenImpCasts(); + + if (isa<DeclRefExpr>(ExprNode)) + return true; + + if (const auto *MemberPointer = dyn_cast<BinaryOperator>(ExprNode)) + return MemberPointer->isPtrMemOp() && + isSupportedMemcpyObjectExpr(MemberPointer->getLHS()); + + if (const auto *Member = dyn_cast<MemberExpr>(ExprNode)) + return isa<FieldDecl>(Member->getMemberDecl()) && + isSupportedMemcpyObjectExpr(Member->getBase()); + + return false; +} + +static const Expr *extractMemcpyObjectExpr(const Expr *ExprNode) { + ExprNode = ExprNode->IgnoreParenCasts(); + 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 isBitCastableMemcpyObjectType(QualType Type, + const ASTContext &Context) { + Type = Type.getCanonicalType().getNonReferenceType(); + return !Type.isNull() && Type.isTriviallyCopyableType(Context); +} + +static bool canAssignBitCastResult(QualType Type) { + Type = Type.getCanonicalType().getNonReferenceType(); + if (Type.isConstQualified() || Type->isArrayType() || + (Type.isVolatileQualified() && Type->isRecordType())) + return false; + + const auto *Record = Type->getAsCXXRecordDecl(); + return !Record || Record->hasSimpleCopyAssignment() || + Record->hasSimpleMoveAssignment(); +} + +static bool isSameUnqualifiedCanonicalType(QualType LHS, QualType RHS) { + return LHS.getCanonicalType().getUnqualifiedType() == + RHS.getCanonicalType().getUnqualifiedType(); +} + +static bool isUnnameableType(QualType Type) { + const TagDecl *Tag = Type->getAsTagDecl(); + return Tag && !Tag->getIdentifier() && !Tag->getTypedefNameForAnonDecl(); +} + +static bool canUseDecltypeAsBitCastType(const Expr *DstExpr) { + if (const auto *Ref = dyn_cast<DeclRefExpr>(DstExpr)) + if (const auto *Var = dyn_cast<VarDecl>(Ref->getDecl())) + return !Var->getType()->isReferenceType(); + + return isa<MemberExpr>(DstExpr); +} + +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) { + const auto IsCurrentBody = [Current](const Stmt *Body) { + if (Body == Current) + return true; + + // IgnoreUnlessSpelledInSource can make `Current` skip over a parenthesized + // body expression even though the enclosing statement still stores it. + const auto *BodyExpr = dyn_cast_or_null<Expr>(Body); + return BodyExpr && BodyExpr->IgnoreParenImpCasts() == Current; + }; + + return llvm::TypeSwitch<const Stmt *, bool>(Parent) + .Case<CompoundStmt>([&](const CompoundStmt *Block) { + return llvm::any_of(Block->body(), IsCurrentBody); + }) + .Case<IfStmt>([&](const IfStmt *If) { + return IsCurrentBody(If->getThen()) || IsCurrentBody(If->getElse()); + }) + .Case<WhileStmt, DoStmt, ForStmt, CXXForRangeStmt>( + [&](const auto *Loop) { return IsCurrentBody(Loop->getBody()); }) + .Case<LabelStmt, SwitchCase, AttributedStmt>([&](const auto *Wrapper) { + return IsCurrentBody(Wrapper->getSubStmt()); + }) + .Default(false); +} + +namespace { + +// Accept only discarded-value uses of the memcpy call: +// memcpy(...); +// (void)memcpy(...); +// (memcpy(...), rhs); +// (lhs, memcpy(...)); if the enclosing comma expression is discarded +// (void)(lhs, memcpy(...)); +// Skip transparent wrappers on the way up and reject any other parent shape. +AST_MATCHER(CallExpr, hasBitCastReplacementContext) { + const Stmt *Current = &Node; + bool SawDiscardedCommaRHS = false; + const CastExpr *DirectVoidCast = nullptr; + const BinaryOperator *CommaContext = nullptr; + const BinaryOperator *OverloadableCommaContext = nullptr; + const auto IsTransparentReplacementParent = [](const Expr *ExprNode) { + return isa<ExprWithCleanups, ImplicitCastExpr, MaterializeTemporaryExpr, + CXXBindTemporaryExpr, ParenExpr>(ExprNode); + }; + const auto BindReplacementContext = [&](const Expr &ReplacementRoot) { + Builder->setBinding("replacementRoot", + DynTypedNode::create(ReplacementRoot)); + if (CommaContext) + Builder->setBinding("commaContext", DynTypedNode::create(*CommaContext)); + if (OverloadableCommaContext) + Builder->setBinding("overloadableCommaContext", + DynTypedNode::create(*OverloadableCommaContext)); + return true; + }; + const auto RecordCommaContext = [&](const BinaryOperator *Comma, + const Expr *Sibling) { + CommaContext = Comma; + const QualType SiblingType = Sibling->getType(); + if (Sibling->isTypeDependent() || SiblingType.isNull() || + SiblingType->isOverloadableType()) + OverloadableCommaContext = Comma; + }; + const auto IsCurrentOperand = [&](const Expr *Operand) { + // The traversal can skip parentheses that the BinaryOperator still owns. + return Operand == Current || Operand->IgnoreParenImpCasts() == Current; + }; + + while (true) { + auto Parents = Finder->getASTContext().getParents(*Current); + if (Parents.size() != 1) + return false; + + if (DirectVoidCast) { + if (const auto *ParentExpr = Parents[0].get<Expr>()) { + if (IsTransparentReplacementParent(ParentExpr)) { + Current = ParentExpr; + continue; + } + } else if (const auto *ParentStmt = Parents[0].get<Stmt>()) { + if (isStatementBody(Current, ParentStmt)) + return BindReplacementContext(*DirectVoidCast); + } + + Builder->setBinding("preservedVoidCast", + DynTypedNode::create(*DirectVoidCast)); + return BindReplacementContext(Node); + } + + if (const auto *ParentExpr = Parents[0].get<Expr>()) { + if (IsTransparentReplacementParent(ParentExpr)) { + Current = ParentExpr; + continue; + } + + if (const auto *Cast = dyn_cast<CastExpr>(ParentExpr)) { + if (Cast->getCastKind() != CK_ToVoid) + return false; + if (!SawDiscardedCommaRHS) + DirectVoidCast = Cast; + + Current = Cast; + continue; + } + + const auto *Comma = dyn_cast<BinaryOperator>(ParentExpr); + if (!Comma || Comma->getOpcode() != BO_Comma) + return false; + if (IsCurrentOperand(Comma->getLHS())) { + RecordCommaContext(Comma, Comma->getRHS()); + return BindReplacementContext(Node); + } + if (!IsCurrentOperand(Comma->getRHS())) + return false; + + // A memcpy on the right-hand side of `,` is safe only if the enclosing + // comma expression is itself discarded, so keep walking from the comma + // node. Remember every sibling because changing the memcpy result type + // can make an overloaded comma viable at any level. + RecordCommaContext(Comma, Comma->getLHS()); + SawDiscardedCommaRHS = true; + Current = Comma; + continue; + } + + const auto *ParentStmt = Parents[0].get<Stmt>(); + if (!ParentStmt || !isStatementBody(Current, ParentStmt)) + return false; + + return BindReplacementContext(Node); + } +} + +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(); + + if (!isBitCastableMemcpyObjectType(DstType, Context) || + !isBitCastableMemcpyObjectType(SrcType, Context) || + !canAssignBitCastResult(DstType) || + isSameUnqualifiedCanonicalType(SrcType, DstType) || + !isMatchingSizeOfExpression(Node.getArg(2), SrcType, DstType, Context)) + return false; + + Builder->setBinding("dstExpr", DynTypedNode::create(*DstExpr)); + Builder->setBinding("srcExpr", DynTypedNode::create(*SrcExpr)); + return true; +} + +AST_MATCHER(FunctionDecl, hasSizeTypeThirdParameter) { + const auto *Type = Node.getType()->getAs<FunctionProtoType>(); + return Type && Type->getNumParams() == 3 && + Finder->getASTContext().hasSameType( + Type->getParamType(2), Finder->getASTContext().getSizeType()); +} + +} // namespace + +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) { + const auto MemcpyDecl = functionDecl( + hasAnyName("::memcpy", "::std::memcpy"), parameterCountIs(3), + returns(pointerType(pointee(voidType()))), + hasParameter(0, hasType(pointerType(pointee(voidType())))), + hasParameter(1, hasType(pointerType( + pointee(qualType(isConstQualified(), voidType()))))), + hasSizeTypeThirdParameter()); + Finder->addMatcher(callExpr(callee(MemcpyDecl), isBitCastMemcpyCandidate(), + hasBitCastReplacementContext()) + .bind("memcpy"), + this); +} + +void UseBitCastCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MemcpyCall = Result.Nodes.getNodeAs<CallExpr>("memcpy"); + const auto *DstExpr = Result.Nodes.getNodeAs<Expr>("dstExpr"); + const auto *SrcExpr = Result.Nodes.getNodeAs<Expr>("srcExpr"); + const auto *ReplacementRoot = Result.Nodes.getNodeAs<Expr>("replacementRoot"); + const auto *CommaContext = + Result.Nodes.getNodeAs<BinaryOperator>("commaContext"); + const auto *OverloadableCommaContext = + Result.Nodes.getNodeAs<BinaryOperator>("overloadableCommaContext"); + const auto *PreservedVoidCast = + Result.Nodes.getNodeAs<CastExpr>("preservedVoidCast"); + assert(MemcpyCall && "memcpy call must be bound"); + assert(DstExpr && "destination expression must be bound"); + assert(SrcExpr && "source expression must be bound"); + assert(ReplacementRoot && "replacement root must be bound"); + + const SourceManager &SM = *Result.SourceManager; + StringRef DstText = tooling::fixit::getText(*DstExpr, *Result.Context); + StringRef SrcText = tooling::fixit::getText(*SrcExpr, *Result.Context); + if (DstText.empty() || SrcText.empty()) + return; + + const PrintingPolicy &Policy = Result.Context->getPrintingPolicy(); + const QualType DstType = + DstExpr->getType().getNonReferenceType().getUnqualifiedType(); + const bool UseDecltype = isUnnameableType(DstType); + if (UseDecltype && !canUseDecltypeAsBitCastType(DstExpr)) { + diag(MemcpyCall->getBeginLoc(), + "use 'std::bit_cast' instead of 'memcpy' for type punning"); + return; + } + const std::string DstTypeName = + UseDecltype ? llvm::formatv("decltype({0})", DstText).str() + : DstType.getAsString(Policy); + const std::string Replacement = [&]() -> std::string { + std::string Assignment = llvm::formatv("{0} = std::bit_cast<{1}>({2})", + DstText, DstTypeName, SrcText) + .str(); + if (PreservedVoidCast) + return llvm::formatv("({0})", Assignment).str(); + if (CommaContext && + (OverloadableCommaContext || DstType->isOverloadableType())) + return llvm::formatv("(void)({0})", Assignment).str(); + return Assignment; + }(); + + const DiagnosticBuilder Diag = + diag(MemcpyCall->getBeginLoc(), + "use 'std::bit_cast' instead of 'memcpy' for type punning"); + Diag << tooling::fixit::createReplacement(*ReplacementRoot, 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..f724dbb9be5b9 --- /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 ``memcpy``-based type punning that can be rewritten as +/// ``std::bit_cast`` in C++20 and later. +/// +/// 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.md b/clang-tools-extra/docs/ReleaseNotes.md index 70dd45eb3297c..79b64fdd198b4 100644 --- a/clang-tools-extra/docs/ReleaseNotes.md +++ b/clang-tools-extra/docs/ReleaseNotes.md @@ -107,6 +107,12 @@ infrastructure are described first, followed by tool-specific sections. Detects malformed regex patterns defined in a single string literal. +- New {doc}`modernize-use-bit-cast + <clang-tidy/checks/modernize/use-bit-cast>` check. + + Finds `memcpy`-based type punning that can be rewritten as `std::bit_cast` + in C++20 and later. + - New {doc}`modernize-use-to-underlying <clang-tidy/checks/modernize/use-to-underlying>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.md b/clang-tools-extra/docs/clang-tidy/checks/list.md index ea6f13365e206..5d029c61214a2 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.md +++ b/clang-tools-extra/docs/clang-tidy/checks/list.md @@ -313,6 +313,7 @@ readability/* | {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.md b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-bit-cast.md new file mode 100644 index 0000000000000..526dff539f412 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-bit-cast.md @@ -0,0 +1,47 @@ +```{title} clang-tidy - modernize-use-bit-cast +``` + +# modernize-use-bit-cast + +Finds `memcpy`-based type punning that can be rewritten as `std::bit_cast` in +C++20 and later. + +```cpp +float src = 1.0f; +unsigned int dst; +std::memcpy(&dst, &src, sizeof(src)); +``` + +This is rewritten to: + +```cpp +float src = 1.0f; +unsigned int dst; +dst = std::bit_cast<unsigned int>(src); +``` + +The fix replaces only the `memcpy` call. It does not rewrite a preceding +declaration into `auto dst = ...`. + +It matches only object-to-object copies where: + +- both object types are trivially copyable, +- the destination can be assigned from `std::bit_cast`, so raw C array + destinations are excluded, +- the source and destination are not the same type after removing aliases and + cv-qualifiers, +- the size argument is `sizeof` of either copied type, and +- the `memcpy` result is not used. + +It intentionally does not diagnose macro expansions, dependent template +cases, unevaluated contexts, or unrelated overloads such as a user-defined +`memcpy`. + +If needed, the fix also inserts `#include <bit>`. + +## Options + +### IncludeStyle + +A string specifying which include style is used, `llvm` or `google`. The +default is `llvm`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-bit-cast/header.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-bit-cast/header.h new file mode 100644 index 0000000000000..b1e4156649dc1 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-bit-cast/header.h @@ -0,0 +1,14 @@ +#ifndef LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_MODERNIZE_INPUTS_USE_BIT_CAST_HEADER_H +#define LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_MODERNIZE_INPUTS_USE_BIT_CAST_HEADER_H + +// CHECK-FIXES: #include <bit> + +inline void header_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); +} + +#endif // LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_MODERNIZE_INPUTS_USE_BIT_CAST_HEADER_H diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast-header.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast-header.cpp new file mode 100644 index 0000000000000..6c251f3a76581 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast-header.cpp @@ -0,0 +1,13 @@ +// RUN: %check_clang_tidy -std=c++20-or-later \ +// RUN: -check-header %S/Inputs/use-bit-cast/header.h \ +// RUN: %s modernize-use-bit-cast %t -- \ +// RUN: -- -I%S/Inputs/use-bit-cast + +void *memcpy(void *To, const void *From, unsigned long long Size); + +namespace std { +using ::memcpy; +} + +#include "header.h" +#include "header.h" diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast-overload.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast-overload.cpp new file mode 100644 index 0000000000000..07c196cc6927f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast-overload.cpp @@ -0,0 +1,22 @@ +// RUN: %check_clang_tidy -std=c++20-or-later %s modernize-use-bit-cast %t + +void *memcpy(void *To, const void *From, int Size); + +namespace std { +void *memcpy(void *To, const void *From, __SIZE_TYPE__ Size); +} + +void nonstandard_size_parameter_case() { + float src = 1.0f; + unsigned int dst; + ::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void standard_size_parameter_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 + // CHECK-FIXES: dst = std::bit_cast<unsigned int>(src); +} 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..60f180e44a158 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-bit-cast.cpp @@ -0,0 +1,538 @@ +// 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, __SIZE_TYPE__ Size); + +namespace std { +template <typename T, unsigned long long N> +struct array { + T Storage[N]; +}; + +using ::memcpy; +} + +template <typename T> +struct identity { + using type = T; +}; + +struct NonTrivial { + NonTrivial(); + NonTrivial(const NonTrivial &); + int Value; +}; + +struct CommaSource { + unsigned int Value; +}; + +struct CommaDestination { + unsigned int Value; +}; + +void *memcpy(CommaDestination *, const CommaSource *, __SIZE_TYPE__); + +enum class CommaSourceEnum : unsigned int {}; +enum class CommaDestinationEnum : unsigned int {}; + +namespace rhs_adl { +struct Token {}; +int operator,(unsigned int, Token); +} // namespace rhs_adl + +namespace lhs_adl { +struct Token {}; +int operator,(Token, unsigned int); +} // namespace lhs_adl + +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 sizeof_type_source_case() { + float src = 1.0f; + unsigned int dst; + std::memcpy(&dst, &src, sizeof(float)); + // 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 sizeof_type_destination_case() { + float src = 1.0f; + unsigned int dst; + std::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 sizeof_dereferenced_source_pointer_case() { + float src = 1.0f; + float *srcp = &src; + unsigned int dst; + std::memcpy(&dst, &src, sizeof(*srcp)); + // 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); + std::memcpy(&dst, srcp, sizeof(*srcp)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void sizeof_dereferenced_destination_pointer_case() { + float src = 1.0f; + unsigned int dst; + unsigned int *dstp = &dst; + std::memcpy(&dst, &src, sizeof(*dstp)); + // 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); + std::memcpy(dstp, &src, sizeof(*dstp)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +void std_array_case() { + std::array<float, 1> src{{1.0f}}; + std::array<unsigned int, 1> 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<std::array<unsigned int, 1>>(src); +} + +void raw_array_source_case() { + float src[1] = {1.0f}; + std::array<unsigned int, 1> 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<std::array<unsigned int, 1>>(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(); +} + +struct OneByte { + unsigned char Value; +}; + +void anonymous_destination_case() { + OneByte src{0}; + struct { + unsigned char Value; + } 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<decltype(dst)>(src); +} + +struct AnonymousMemberHolder { + struct { + unsigned char Value; + } dst; +}; + +void anonymous_member_destination_case() { + OneByte src{0}; + AnonymousMemberHolder holder{}; + std::memcpy(&holder.dst, &src, sizeof(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: holder.dst = std::bit_cast<decltype(holder.dst)>(src); +} + +void lambda_destination_case() { + OneByte src{0}; + auto 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<decltype(dst)>(src); +} + +void lambda_reference_destination_case() { + OneByte src{0}; + auto storage = [] {}; + auto &dst = storage; + std::memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: std::memcpy(&dst, &src, sizeof(src)); +} + +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 = (dst = std::bit_cast<unsigned int>(src), 42); +} + +void comma_rhs_case() { + float src = 1.0f; + unsigned int dst; + (0, std::memcpy(&dst, &src, sizeof(src))); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: (0, dst = std::bit_cast<unsigned int>(src)); +} + +void comma_record_destination_case() { + CommaSource src{0}; + CommaDestination dst{0}; + 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<CommaDestination>(src)), 42); +} + +void comma_enum_destination_case() { + CommaSourceEnum src{}; + CommaDestinationEnum 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<CommaDestinationEnum>(src)), 42); +} + +void comma_rhs_adl_case() { + float src = 1.0f; + unsigned int dst; + auto value = (std::memcpy(&dst, &src, sizeof(src)), rhs_adl::Token{}); + (void)value; + // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: auto value = ((void)(dst = std::bit_cast<unsigned int>(src)), rhs_adl::Token{}); +} + +void comma_lhs_adl_case() { + float src = 1.0f; + unsigned int dst; + (lhs_adl::Token{}, std::memcpy(&dst, &src, sizeof(src))); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: (lhs_adl::Token{}, (void)(dst = std::bit_cast<unsigned int>(src))); +} + +void nested_comma_adl_case() { + float src = 1.0f; + unsigned int dst; + (0, (lhs_adl::Token{}, std::memcpy(&dst, &src, sizeof(src)))); + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: (0, (lhs_adl::Token{}, (void)(dst = std::bit_cast<unsigned int>(src)))); +} + +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: dst = std::bit_cast<unsigned int>(src); +} + +void void_cast_conditional_case(bool Cond) { + float src = 1.0f; + unsigned int dst; + Cond ? (void)std::memcpy(&dst, &src, sizeof(src)) : (void)0; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: Cond ? (void)(dst = std::bit_cast<unsigned int>(src)) : (void)0; +} + +void void_cast_comma_case() { + float src = 1.0f; + unsigned int dst; + ((void)std::memcpy(&dst, &src, sizeof(src)), rhs_adl::Token{}); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: ((void)(dst = std::bit_cast<unsigned int>(src)), rhs_adl::Token{}); +} + +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 pointer_object_case(float *srcp) { + unsigned int *dstp; + std::memcpy(&dstp, &srcp, sizeof(srcp)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: dstp = std::bit_cast<unsigned int *>(srcp); +} + +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 raw_array_destination_case() { + std::array<float, 1> src{{1.0f}}; + unsigned int dst[1]; + std::memcpy(&dst, &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: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning + // CHECK-FIXES: dst = std::bit_cast<unsigned int>(src); +} + +void volatile_destination_case() { + float src = 1.0f; + volatile unsigned int dst; + std::memcpy(const_cast<unsigned int *>(&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 volatile_record_destination_case(CommaSource src) { + volatile CommaDestination dst{0}; + std::memcpy(const_cast<CommaDestination *>(&dst), &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 + +void global_overload_case() { + CommaSource src{0}; + CommaDestination dst{0}; + memcpy(&dst, &src, sizeof(src)); + // CHECK-MESSAGES-NOT: :[[@LINE-1]]:3: warning: use 'std::bit_cast' instead of 'memcpy' for type punning +} + +#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; +} + +template <typename T> +void non_dependent_template_case() { + float src = 1.0f; + unsigned int dst; + 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); +} + +template void non_dependent_template_case<int>(); + +template <typename T> +concept MemcpyInRequires = requires(float &src, unsigned int &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 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
