[clang-tools-extra] [clang-tidy] Add check 'bugprone-cast-to-struct' (PR #153428)
Balázs Kéri via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 15 08:01:36 PDT 2025
https://github.com/balazske updated https://github.com/llvm/llvm-project/pull/153428
>From 95634ba1fa9690f3d95c45005c908e642c1ced79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Wed, 13 Aug 2025 15:13:13 +0200
Subject: [PATCH 1/2] [clang-tidy] Add check 'bugprone-cast-to-struct'
---
.../bugprone/BugproneTidyModule.cpp | 2 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../clang-tidy/bugprone/CastToStructCheck.cpp | 82 +++++++++++++++++++
.../clang-tidy/bugprone/CastToStructCheck.h | 39 +++++++++
clang-tools-extra/docs/ReleaseNotes.rst | 5 ++
.../checks/bugprone/cast-to-struct.rst | 54 ++++++++++++
.../docs/clang-tidy/checks/list.rst | 1 +
.../checkers/bugprone/cast-to-struct-ignore.c | 40 +++++++++
.../checkers/bugprone/cast-to-struct.c | 66 +++++++++++++++
9 files changed, 290 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 824ebdfbd00dc..5a1573eb34ee3 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -18,6 +18,7 @@
#include "BranchCloneCheck.h"
#include "CapturingThisInMemberVariableCheck.h"
#include "CastingThroughVoidCheck.h"
+#include "CastToStructCheck.h"
#include "ChainedComparisonCheck.h"
#include "ComparePointerToMemberVirtualFunctionCheck.h"
#include "CopyConstructorInitCheck.h"
@@ -125,6 +126,7 @@ class BugproneModule : public ClangTidyModule {
"bugprone-capturing-this-in-member-variable");
CheckFactories.registerCheck<CastingThroughVoidCheck>(
"bugprone-casting-through-void");
+ CheckFactories.registerCheck<CastToStructCheck>("bugprone-cast-to-struct");
CheckFactories.registerCheck<ChainedComparisonCheck>(
"bugprone-chained-comparison");
CheckFactories.registerCheck<ComparePointerToMemberVirtualFunctionCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 59928e5e47a09..c2bd0d1e3456c 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -14,6 +14,7 @@ add_clang_library(clangTidyBugproneModule STATIC
BugproneTidyModule.cpp
CapturingThisInMemberVariableCheck.cpp
CastingThroughVoidCheck.cpp
+ CastToStructCheck.cpp
ChainedComparisonCheck.cpp
ComparePointerToMemberVirtualFunctionCheck.cpp
CopyConstructorInitCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp
new file mode 100644
index 0000000000000..374736d48d1c8
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp
@@ -0,0 +1,82 @@
+//===--- CastToStructCheck.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 "CastToStructCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+CastToStructCheck::CastToStructCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoredFunctions(
+ utils::options::parseStringList(Options.get("IgnoredFunctions", ""))),
+ IgnoredFromTypes(
+ utils::options::parseStringList(Options.get("IgnoredFromTypes", ""))),
+ IgnoredToTypes(
+ utils::options::parseStringList(Options.get("IgnoredToTypes", ""))) {}
+
+void CastToStructCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoredFunctions",
+ utils::options::serializeStringList(IgnoredFunctions));
+ Options.store(Opts, "IgnoredFromTypes",
+ utils::options::serializeStringList(IgnoredFromTypes));
+ Options.store(Opts, "IgnoredToTypes",
+ utils::options::serializeStringList(IgnoredToTypes));
+}
+
+void CastToStructCheck::registerMatchers(MatchFinder *Finder) {
+ auto FromPointee =
+ qualType(hasUnqualifiedDesugaredType(type().bind("FromType")),
+ unless(qualType(matchers::matchesAnyListedTypeName(
+ IgnoredFromTypes, false))))
+ .bind("FromPointee");
+ auto ToPointee =
+ qualType(hasUnqualifiedDesugaredType(recordType().bind("ToType")),
+ unless(qualType(
+ matchers::matchesAnyListedTypeName(IgnoredToTypes, false))))
+ .bind("ToPointee");
+ auto FromPtrType = qualType(pointsTo(FromPointee)).bind("FromPtr");
+ auto ToPtrType = qualType(pointsTo(ToPointee)).bind("ToPtr");
+ Finder->addMatcher(
+ cStyleCastExpr(hasSourceExpression(hasType(FromPtrType)),
+ hasType(ToPtrType),
+ unless(hasAncestor(functionDecl(
+ matchers::matchesAnyListedName(IgnoredFunctions)))))
+ .bind("CastExpr"),
+ this);
+}
+
+void CastToStructCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *const FoundCastExpr =
+ Result.Nodes.getNodeAs<CStyleCastExpr>("CastExpr");
+ const auto *const FromPtr = Result.Nodes.getNodeAs<QualType>("FromPtr");
+ const auto *const ToPtr = Result.Nodes.getNodeAs<QualType>("ToPtr");
+ const auto *const FromPointee =
+ Result.Nodes.getNodeAs<QualType>("FromPointee");
+ const auto *const ToPointee = Result.Nodes.getNodeAs<QualType>("ToPointee");
+ const auto *const FromType = Result.Nodes.getNodeAs<Type>("FromType");
+ const auto *const ToType = Result.Nodes.getNodeAs<RecordType>("ToType");
+ if (!FromPointee || !ToPointee)
+ return;
+ if (FromType->isVoidType() || FromType->isUnionType() ||
+ ToType->isUnionType())
+ return;
+ if (FromType == ToType)
+ return;
+ diag(FoundCastExpr->getExprLoc(),
+ "casting a %0 pointer to a "
+ "%1 pointer and accessing a field can lead to memory "
+ "access errors or data corruption")
+ << *FromPtr << *ToPtr;
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h
new file mode 100644
index 0000000000000..9e6c868824d23
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h
@@ -0,0 +1,39 @@
+//===--- CastToStructCheck.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_CASTTOSTRUCTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTTOSTRUCTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Finds casts from pointers to struct or scalar type to pointers to struct
+/// type.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/cast-to-struct.html
+class CastToStructCheck : public ClangTidyCheck {
+public:
+ CastToStructCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.C99;
+ }
+
+private:
+ std::vector<llvm::StringRef> IgnoredFunctions;
+ std::vector<llvm::StringRef> IgnoredFromTypes;
+ std::vector<llvm::StringRef> IgnoredToTypes;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CASTTOSTRUCTCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 187aae2ec8c90..a13072123b9ef 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -112,6 +112,11 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^
+- New :doc:`bugprone-cast-to-struct
+ <clang-tidy/checks/bugprone/cast-to-struct>` check.
+
+ Finds casts from pointers to struct or scalar type to pointers to struct type.
+
- New :doc:`bugprone-invalid-enum-default-initialization
<clang-tidy/checks/bugprone/invalid-enum-default-initialization>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst
new file mode 100644
index 0000000000000..79864935d3ea1
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst
@@ -0,0 +1,54 @@
+.. title:: clang-tidy - bugprone-cast-to-struct
+
+bugprone-cast-to-struct
+=======================
+
+Finds casts from pointers to struct or scalar type to pointers to struct type.
+
+Casts between pointers to different structs can be unsafe because it is possible
+to access uninitialized or undefined data after the cast. There may be issues
+with type compatibility or data alignment. Cast from a pointer to a scalar type
+(which points often to an array or memory block) to a `struct` type pointer can
+be unsafe for similar reasons. This check warns at casts from any non-`struct`
+type to a `struct` type. No warning is produced at cast from type `void *` (this
+is the usual way of allocating memory with `malloc`-like functions). It is
+possible to specify additional types to ignore by the check. In addition,
+`union` types are completely excluded from the check. The check does not take
+into account type compatibility or data layout, only the names of the types.
+
+.. code-block:: c
+
+ void test1(char *p) {
+ struct S1 *s;
+ s = (struct S1 *)p; // warn: 'char *' is converted to 'struct S1 *'
+ }
+
+ void test2(struct S1 *p) {
+ struct S2 *s;
+ s = (struct S2 *)p; // warn: 'struct S1 *' is converted to 'struct S2 *'
+ }
+
+ void test3(void) {
+ struct S1 *s;
+ s = (struct S1 *)calloc(1, sizeof(struct S1)); // no warning
+ }
+
+Options
+-------
+
+.. option:: IgnoredFromTypes
+
+ Semicolon-separated list of types for which the checker should not warn if
+ encountered at cast source. Can contain regular expressions. The `*`
+ character (for pointer type) is not needed in the type names.
+
+.. option:: IgnoredToTypes
+
+ Semicolon-separated list of types for which the checker should not warn if
+ encountered at cast destination. Can contain regular expressions. The `*`
+ character (for pointer type) is not needed in the type names.
+
+.. option:: IgnoredFunctions
+
+ List of function names from which the checker should produce no warnings. Can
+ contain regular expressions.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index b6444eb3c9aec..5f8ae4a6eabb7 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -85,6 +85,7 @@ Clang-Tidy Checks
:doc:`bugprone-bool-pointer-implicit-conversion <bugprone/bool-pointer-implicit-conversion>`, "Yes"
:doc:`bugprone-branch-clone <bugprone/branch-clone>`,
:doc:`bugprone-capturing-this-in-member-variable <bugprone/capturing-this-in-member-variable>`,
+ :doc:`bugprone-cast-to-struct <bugprone/cast-to-struct>`,
:doc:`bugprone-casting-through-void <bugprone/casting-through-void>`,
:doc:`bugprone-chained-comparison <bugprone/chained-comparison>`,
:doc:`bugprone-compare-pointer-to-member-virtual-function <bugprone/compare-pointer-to-member-virtual-function>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c
new file mode 100644
index 0000000000000..1cc4744220f4a
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c
@@ -0,0 +1,40 @@
+// RUN: %check_clang_tidy -check-suffixes=FUNC %s bugprone-cast-to-struct %t -- \
+// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredFunctions: 'ignored_f$'}}"
+// RUN: %check_clang_tidy -check-suffixes=FROM-TY %s bugprone-cast-to-struct %t -- \
+// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredFromTypes: 'int'}}"
+// RUN: %check_clang_tidy -check-suffixes=TO-TY %s bugprone-cast-to-struct %t -- \
+// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredToTypes: 'IgnoredType'}}"
+
+struct IgnoredType {
+ int a;
+};
+
+struct OtherType {
+ int a;
+ int b;
+};
+
+void ignored_f(char *p) {
+ struct OtherType *p1;
+ p1 = (struct OtherType *)p;
+ // CHECK-MESSAGES-FROM-TY: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption
+ // CHECK-MESSAGES-TO-TY: :[[@LINE-2]]:8: warning: casting a 'char *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption
+}
+
+void ignored_from_type(int *p) {
+ struct OtherType *p1;
+ p1 = (struct OtherType *)p;
+ // CHECK-MESSAGES-FUNC: :[[@LINE-1]]:8: warning: casting a 'int *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption
+ // CHECK-MESSAGES-TO-TY: :[[@LINE-2]]:8: warning: casting a 'int *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption
+}
+
+void ignored_to_type(char *p) {
+ struct IgnoredType *p1;
+ p1 = (struct IgnoredType *)p;
+ // CHECK-MESSAGES-FUNC: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct IgnoredType *' pointer and accessing a field can lead to memory access errors or data corruption
+ // CHECK-MESSAGES-FROM-TY: :[[@LINE-2]]:8: warning: casting a 'char *' pointer to a 'struct IgnoredType *' pointer and accessing a field can lead to memory access errors or data corruption
+}
+
+struct OtherType *test_void_is_always_ignored(void *p) {
+ return (struct OtherType *)p;
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c
new file mode 100644
index 0000000000000..cfabf69351f39
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c
@@ -0,0 +1,66 @@
+// RUN: %check_clang_tidy %s bugprone-cast-to-struct %t
+
+struct S1 {
+ int a;
+};
+
+struct S2 {
+ char a;
+};
+
+union U1 {
+ int a;
+ char b;
+};
+
+union U2 {
+ struct S1 a;
+ char b;
+};
+
+typedef struct S1 TyS1;
+typedef struct S1 *TyPS1;
+
+typedef union U1 *TyPU1;
+
+typedef int int_t;
+typedef int * int_ptr_t;
+
+struct S1 *test_simple(char *p) {
+ return (struct S1 *)p;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: casting a 'char *' pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct]
+ struct S1 *s;
+ int i;
+ s = (struct S1 *)&i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: casting a 'int *' pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct]
+}
+
+struct S1 *test_cast_from_void(void *p) {
+ return (struct S1 *)p;
+}
+
+struct S1 *test_cast_from_struct(struct S2 *p) {
+ return (struct S1 *)p;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: casting a 'struct S2 *' pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct]
+}
+
+TyPS1 test_cast_from_similar(struct S1 *p) {
+ return (TyPS1)p;
+}
+
+void test_typedef(char *p1, int_t *p2, int_ptr_t p3) {
+ TyS1 *a = (TyS1 *)p1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: casting a 'char *' pointer to a 'TyS1 *' (aka 'struct S1 *') pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct]
+ TyPS1 b = (TyPS1)p1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: casting a 'char *' pointer to a 'TyPS1' (aka 'struct S1 *') pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct]
+ struct S1 *c = (struct S1 *)p2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: casting a 'int_t *' (aka 'int *') pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct]
+ struct S1 *d = (struct S1 *)p3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: casting a 'int_ptr_t' (aka 'int *') pointer to a 'struct S1 *' pointer and accessing a field can lead to memory access errors or data corruption [bugprone-cast-to-struct]
+}
+
+void test_union(char *p1, union U1 *p2, TyPU1 p3) {
+ union U1 *a = (union U1 *)p1;
+ struct S1 *b = (struct S1 *)p2;
+ struct S1 *c = (struct S1 *)p3;
+}
>From f2eb00df8326548801b019450ed36f3ff4d476c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.keri at ericsson.com>
Date: Fri, 15 Aug 2025 16:21:23 +0200
Subject: [PATCH 2/2] updated documentation, changed options
---
.../clang-tidy/bugprone/CastToStructCheck.cpp | 58 +++++++++----------
.../clang-tidy/bugprone/CastToStructCheck.h | 4 +-
.../checks/bugprone/cast-to-struct.rst | 54 +++++++++--------
.../checkers/bugprone/cast-to-struct-ignore.c | 51 +++++++---------
4 files changed, 82 insertions(+), 85 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp
index 374736d48d1c8..9915d32e1ddbd 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp
@@ -17,42 +17,31 @@ namespace clang::tidy::bugprone {
CastToStructCheck::CastToStructCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- IgnoredFunctions(
- utils::options::parseStringList(Options.get("IgnoredFunctions", ""))),
- IgnoredFromTypes(
- utils::options::parseStringList(Options.get("IgnoredFromTypes", ""))),
- IgnoredToTypes(
- utils::options::parseStringList(Options.get("IgnoredToTypes", ""))) {}
+ IgnoredCasts(
+ utils::options::parseStringList(Options.get("IgnoredCasts", ""))) {}
void CastToStructCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "IgnoredFunctions",
- utils::options::serializeStringList(IgnoredFunctions));
- Options.store(Opts, "IgnoredFromTypes",
- utils::options::serializeStringList(IgnoredFromTypes));
- Options.store(Opts, "IgnoredToTypes",
- utils::options::serializeStringList(IgnoredToTypes));
+ Options.store(Opts, "IgnoredCasts",
+ utils::options::serializeStringList(IgnoredCasts));
}
void CastToStructCheck::registerMatchers(MatchFinder *Finder) {
auto FromPointee =
qualType(hasUnqualifiedDesugaredType(type().bind("FromType")),
- unless(qualType(matchers::matchesAnyListedTypeName(
- IgnoredFromTypes, false))))
+ unless(voidType()),
+ unless(hasDeclaration(recordDecl(isUnion()))))
.bind("FromPointee");
auto ToPointee =
- qualType(hasUnqualifiedDesugaredType(recordType().bind("ToType")),
- unless(qualType(
- matchers::matchesAnyListedTypeName(IgnoredToTypes, false))))
+ qualType(hasUnqualifiedDesugaredType(
+ recordType(unless(hasDeclaration(recordDecl(isUnion()))))
+ .bind("ToType")))
.bind("ToPointee");
auto FromPtrType = qualType(pointsTo(FromPointee)).bind("FromPtr");
auto ToPtrType = qualType(pointsTo(ToPointee)).bind("ToPtr");
- Finder->addMatcher(
- cStyleCastExpr(hasSourceExpression(hasType(FromPtrType)),
- hasType(ToPtrType),
- unless(hasAncestor(functionDecl(
- matchers::matchesAnyListedName(IgnoredFunctions)))))
- .bind("CastExpr"),
- this);
+ Finder->addMatcher(cStyleCastExpr(hasSourceExpression(hasType(FromPtrType)),
+ hasType(ToPtrType))
+ .bind("CastExpr"),
+ this);
}
void CastToStructCheck::check(const MatchFinder::MatchResult &Result) {
@@ -65,13 +54,24 @@ void CastToStructCheck::check(const MatchFinder::MatchResult &Result) {
const auto *const ToPointee = Result.Nodes.getNodeAs<QualType>("ToPointee");
const auto *const FromType = Result.Nodes.getNodeAs<Type>("FromType");
const auto *const ToType = Result.Nodes.getNodeAs<RecordType>("ToType");
- if (!FromPointee || !ToPointee)
- return;
- if (FromType->isVoidType() || FromType->isUnionType() ||
- ToType->isUnionType())
- return;
+
if (FromType == ToType)
return;
+
+ const std::string FromName = FromPointee->getAsString();
+ const std::string ToName = ToPointee->getAsString();
+ llvm::Regex FromR;
+ llvm::Regex ToR;
+ for (auto [Idx, Str] : llvm::enumerate(IgnoredCasts)) {
+ if (Idx % 2 == 0) {
+ FromR = llvm::Regex(Str);
+ } else {
+ ToR = llvm::Regex(Str);
+ if (FromR.match(FromName) && ToR.match(ToName))
+ return;
+ }
+ }
+
diag(FoundCastExpr->getExprLoc(),
"casting a %0 pointer to a "
"%1 pointer and accessing a field can lead to memory "
diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h
index 9e6c868824d23..ec89fa8ab6dfc 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h
@@ -29,9 +29,7 @@ class CastToStructCheck : public ClangTidyCheck {
}
private:
- std::vector<llvm::StringRef> IgnoredFunctions;
- std::vector<llvm::StringRef> IgnoredFromTypes;
- std::vector<llvm::StringRef> IgnoredToTypes;
+ std::vector<llvm::StringRef> IgnoredCasts;
};
} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst
index 79864935d3ea1..9b9afa43b6a96 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst
@@ -6,15 +6,15 @@ bugprone-cast-to-struct
Finds casts from pointers to struct or scalar type to pointers to struct type.
Casts between pointers to different structs can be unsafe because it is possible
-to access uninitialized or undefined data after the cast. There may be issues
-with type compatibility or data alignment. Cast from a pointer to a scalar type
-(which points often to an array or memory block) to a `struct` type pointer can
-be unsafe for similar reasons. This check warns at casts from any non-`struct`
-type to a `struct` type. No warning is produced at cast from type `void *` (this
-is the usual way of allocating memory with `malloc`-like functions). It is
-possible to specify additional types to ignore by the check. In addition,
-`union` types are completely excluded from the check. The check does not take
-into account type compatibility or data layout, only the names of the types.
+to access uninitialized or undefined data after the cast. Cast from a
+scalar-type pointer (which points often to an array or memory block) to a
+``struct`` type pointer can be unsafe for similar reasons. This check warns at
+pointer casts from any non-struct type to a struct type. No warning is produced
+at cast from type ``void *`` (this is the usual way of allocating memory with
+``malloc``-like functions). In addition, ``union`` types are excluded from the
+check. It is possible to specify additional types to ignore. The check does not
+take into account type compatibility or data layout, only the names of the
+types.
.. code-block:: c
@@ -33,22 +33,28 @@ into account type compatibility or data layout, only the names of the types.
s = (struct S1 *)calloc(1, sizeof(struct S1)); // no warning
}
-Options
--------
-
-.. option:: IgnoredFromTypes
+Limitations
+-----------
- Semicolon-separated list of types for which the checker should not warn if
- encountered at cast source. Can contain regular expressions. The `*`
- character (for pointer type) is not needed in the type names.
+The check does run only on `C` code.
-.. option:: IgnoredToTypes
+C-style casts are discouraged in `C++` and should be converted to more type-safe
+casts. The ``reinterpreted_cast`` is used for the most unsafe cases and
+indicates by itself a potentially dangerous operation. Additionally, inheritance
+and dynamic types would make such a check less useful.
- Semicolon-separated list of types for which the checker should not warn if
- encountered at cast destination. Can contain regular expressions. The `*`
- character (for pointer type) is not needed in the type names.
-
-.. option:: IgnoredFunctions
+Options
+-------
- List of function names from which the checker should produce no warnings. Can
- contain regular expressions.
+.. option:: IgnoredCasts
+
+ Can contain a semicolon-separated list of type names that specify cast
+ types to ignore. The list should contain pairs of type names in a way that
+ the first type is the "from" type, the second is the "to" type in a cast
+ expression. The types in a pair and the pairs itself are separated by
+ ``;`` characters. For example ``char;Type1;char;Type2`` specifies that the
+ check does not produce warning for casts from ``char *`` to ``Type1 *`` and
+ casts from ``char *`` to ``Type2 *``. The list entries can be regular
+ expressions. The type name in the cast expression is matched without
+ resolution of type aliases like ``typedef``. Default value is empty list.
+ (Casts from ``void *`` are ignored always regardless of this list.)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c
index 1cc4744220f4a..e64fe177448d9 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct-ignore.c
@@ -1,40 +1,33 @@
-// RUN: %check_clang_tidy -check-suffixes=FUNC %s bugprone-cast-to-struct %t -- \
-// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredFunctions: 'ignored_f$'}}"
-// RUN: %check_clang_tidy -check-suffixes=FROM-TY %s bugprone-cast-to-struct %t -- \
-// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredFromTypes: 'int'}}"
-// RUN: %check_clang_tidy -check-suffixes=TO-TY %s bugprone-cast-to-struct %t -- \
-// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredToTypes: 'IgnoredType'}}"
+// RUN: %check_clang_tidy %s bugprone-cast-to-struct %t -- \
+// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredCasts: 'char;S1;int;Other*'}}"
-struct IgnoredType {
+struct S1 {
int a;
};
-struct OtherType {
+struct S2 {
+ char a;
+};
+
+struct OtherS {
int a;
int b;
};
-void ignored_f(char *p) {
- struct OtherType *p1;
- p1 = (struct OtherType *)p;
- // CHECK-MESSAGES-FROM-TY: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption
- // CHECK-MESSAGES-TO-TY: :[[@LINE-2]]:8: warning: casting a 'char *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption
-}
-
-void ignored_from_type(int *p) {
- struct OtherType *p1;
- p1 = (struct OtherType *)p;
- // CHECK-MESSAGES-FUNC: :[[@LINE-1]]:8: warning: casting a 'int *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption
- // CHECK-MESSAGES-TO-TY: :[[@LINE-2]]:8: warning: casting a 'int *' pointer to a 'struct OtherType *' pointer and accessing a field can lead to memory access errors or data corruption
-}
-
-void ignored_to_type(char *p) {
- struct IgnoredType *p1;
- p1 = (struct IgnoredType *)p;
- // CHECK-MESSAGES-FUNC: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct IgnoredType *' pointer and accessing a field can lead to memory access errors or data corruption
- // CHECK-MESSAGES-FROM-TY: :[[@LINE-2]]:8: warning: casting a 'char *' pointer to a 'struct IgnoredType *' pointer and accessing a field can lead to memory access errors or data corruption
+void test1(char *p1, int *p2) {
+ struct S1 *s1;
+ s1 = (struct S1 *)p1;
+ struct S2 *s2;
+ s2 = (struct S2 *)p1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct S2 *'
+ s2 = (struct S2 *)p2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'int *' pointer to a 'struct S2 *'
+ struct OtherS *s3;
+ s3 = (struct OtherS *)p2;
+ s3 = (struct OtherS *)p1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct OtherS *'
}
-struct OtherType *test_void_is_always_ignored(void *p) {
- return (struct OtherType *)p;
+struct S2 *test_void_is_always_ignored(void *p) {
+ return (struct S2 *)p;
}
More information about the cfe-commits
mailing list