[clang-tools-extra] [clang-tidy] Add option to cppcoreguidelines-pro-type-reinterpret-cas… (PR #106922)

Carlos Galvez via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 1 13:06:36 PDT 2024


https://github.com/carlosgalvezp created https://github.com/llvm/llvm-project/pull/106922

…t to allow casts to byte types

These casts are safe according to the Standard, so add an option to allow them and not emit a warning. This helps silencing some noise and focusing on the unsafe casts.

>From 87f3bca5a4f0cdb7f560da1d11d76ee9f3ce9d0a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carlos=20G=C3=A1lvez?= <carlos.galvez at zenseact.com>
Date: Sun, 1 Sep 2024 19:14:22 +0000
Subject: [PATCH] [clang-tidy] Add option to
 cppcoreguidelines-pro-type-reinterpret-cast to allow casts to byte types

These casts are safe according to the Standard, so add an option to
allow them and not emit a warning. This helps silencing some noise
and focusing on the unsafe casts.
---
 .../ProTypeReinterpretCastCheck.cpp           | 50 +++++++++++++++++--
 .../ProTypeReinterpretCastCheck.h             |  7 ++-
 clang-tools-extra/docs/ReleaseNotes.rst       |  4 ++
 .../pro-type-reinterpret-cast.rst             | 19 +++++++
 .../pro-type-reinterpret-cast.cpp             | 36 ++++++++++++-
 5 files changed, 109 insertions(+), 7 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.cpp
index 14456caab612b7..6385dd5440901c 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.cpp
@@ -9,20 +9,64 @@
 #include "ProTypeReinterpretCastCheck.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/STLExtras.h"
+#include <array>
+#include <string>
 
 using namespace clang::ast_matchers;
 
 namespace clang::tidy::cppcoreguidelines {
 
+static bool isCastToBytes(ASTContext const &Ctx,
+                          CXXReinterpretCastExpr const &Expr) {
+  // https://eel.is/c++draft/basic.lval#11.3
+  static constexpr std::array<StringRef, 3> AllowedByteTypes = {
+      "char",
+      "unsigned char",
+      "std::byte",
+  };
+
+  // We only care about pointer casts
+  QualType DestType = Expr.getTypeAsWritten();
+  if (!DestType->isPointerType())
+    return false;
+
+  // Get the unqualified canonical type, and check if it's allowed
+  // We need to wrap the Type into a QualType to call getAsString()
+  const Type *UnqualDestType =
+      DestType.getCanonicalType()->getPointeeType().getTypePtr();
+  std::string DestTypeString = QualType(UnqualDestType, /*Quals=*/0)
+                                   .getAsString(Ctx.getPrintingPolicy());
+  return llvm::any_of(AllowedByteTypes, [DestTypeString](StringRef Type) {
+    return Type == DestTypeString;
+  });
+}
+
+ProTypeReinterpretCastCheck::ProTypeReinterpretCastCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      AllowCastToBytes(Options.getLocalOrGlobal("AllowCastToBytes", false)) {}
+
+void ProTypeReinterpretCastCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "AllowCastToBytes", AllowCastToBytes);
+}
+
 void ProTypeReinterpretCastCheck::registerMatchers(MatchFinder *Finder) {
   Finder->addMatcher(cxxReinterpretCastExpr().bind("cast"), this);
 }
 
 void ProTypeReinterpretCastCheck::check(
     const MatchFinder::MatchResult &Result) {
-  const auto *MatchedCast =
-      Result.Nodes.getNodeAs<CXXReinterpretCastExpr>("cast");
-  diag(MatchedCast->getOperatorLoc(), "do not use reinterpret_cast");
+
+  if (const auto *MatchedCast =
+          Result.Nodes.getNodeAs<CXXReinterpretCastExpr>("cast")) {
+    ASTContext const &Ctx = *Result.Context;
+    if (AllowCastToBytes && isCastToBytes(Ctx, *MatchedCast))
+      return;
+
+    diag(MatchedCast->getOperatorLoc(), "do not use reinterpret_cast");
+  }
 }
 
 } // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.h
index da001bfb85d787..66b46ba7e9f5b9 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.h
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.h
@@ -19,13 +19,16 @@ namespace clang::tidy::cppcoreguidelines {
 /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-type-reinterpret-cast.html
 class ProTypeReinterpretCastCheck : public ClangTidyCheck {
 public:
-  ProTypeReinterpretCastCheck(StringRef Name, ClangTidyContext *Context)
-      : ClangTidyCheck(Name, Context) {}
+  ProTypeReinterpretCastCheck(StringRef Name, ClangTidyContext *Context);
   bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
     return LangOpts.CPlusPlus;
   }
   void registerMatchers(ast_matchers::MatchFinder *Finder) override;
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const bool AllowCastToBytes;
 };
 
 } // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index b001a6ad446695..1b38a3572e02f3 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -104,6 +104,10 @@ New check aliases
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+- Add option to :doc:`cppcoreguidelines-pro-type-reinterpret-cast
+  <clang-tidy/checks/cppcoreguidelines/pro-type-reinterpret-cast>` to allow
+  casting an object to its byte representation.
+
 - Improved :doc:`modernize-use-std-format
   <clang-tidy/checks/modernize/use-std-format>` check to support replacing
   member function calls too.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-reinterpret-cast.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-reinterpret-cast.rst
index a0946825156fcf..c8d9130f4f2534 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-reinterpret-cast.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-reinterpret-cast.rst
@@ -12,3 +12,22 @@ unrelated type ``Z``.
 This rule is part of the `Type safety (Type.1.1)
 <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Pro-type-reinterpretcast>`_
 profile from the C++ Core Guidelines.
+
+Options
+-------
+
+.. option:: AllowCastToBytes
+
+  When this setting is set to `true`, it will not warn when casting an object
+  to its byte representation, which is safe according to the C++ Standard.
+  The allowed byte types are: ``char``, ``unsigned char`` and ``std::byte``.
+  Example:
+
+  .. code-block:: cpp
+
+    float x{};
+    auto a = reinterpret_cast<char*>(&x);           // OK
+    auto b = reinterpret_cast<unsigned char*>(&x);  // OK
+    auto c = reinterpret_cast<std::byte*>(&x);      // OK
+
+  Default value is `false`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-reinterpret-cast.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-reinterpret-cast.cpp
index 54208102e6f07f..1bfdc4b0eca7b8 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-reinterpret-cast.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-reinterpret-cast.cpp
@@ -1,6 +1,38 @@
-// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-reinterpret-cast %t
+// RUN: %check_clang_tidy -check-suffix=DEFAULT             %s cppcoreguidelines-pro-type-reinterpret-cast %t
+// RUN: %check_clang_tidy -check-suffix=ALLOW-CAST-TO-BYTES %s cppcoreguidelines-pro-type-reinterpret-cast %t \
+// RUN:   -config="{CheckOptions: { \
+// RUN:               cppcoreguidelines-pro-type-reinterpret-cast.AllowCastToBytes: True \
+// RUN: }}"
 
 int i = 0;
 void *j;
 void f() { j = reinterpret_cast<void *>(i); }
-// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+// CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:16: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+// CHECK-MESSAGES-ALLOW-CAST-TO-BYTES: :[[@LINE-2]]:16: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+
+namespace std
+{
+enum class byte : unsigned char
+{};
+}
+
+void check_cast_to_bytes()
+{
+  float x{};
+  auto a = reinterpret_cast<char*>(&x);
+  // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+  auto b = reinterpret_cast<char const*>(&x);
+  // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+  auto c = reinterpret_cast<unsigned char*>(&x);
+  // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+  auto d = reinterpret_cast<unsigned char const*>(&x);
+  // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+  auto e = reinterpret_cast<std::byte*>(&x);
+  // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+  auto f = reinterpret_cast<std::byte const*>(&x);
+  // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+
+  using CharPtr = char*;
+  auto g = reinterpret_cast<CharPtr>(&x);
+  // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:12: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
+}



More information about the cfe-commits mailing list