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.k...@ericsson.com> Date: Wed, 13 Aug 2025 15:13:13 +0200 Subject: [PATCH 1/5] [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.k...@ericsson.com> Date: Fri, 15 Aug 2025 16:21:23 +0200 Subject: [PATCH 2/5] 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; } From 43153e3f6841aff0e7fc955794d581e0828ec03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.k...@ericsson.com> Date: Mon, 18 Aug 2025 09:29:37 +0200 Subject: [PATCH 3/5] fixed formatting and documentation --- clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp | 2 +- .../docs/clang-tidy/checks/bugprone/cast-to-struct.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 5a1573eb34ee3..8ea24740b1e1d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -17,8 +17,8 @@ #include "BoolPointerImplicitConversionCheck.h" #include "BranchCloneCheck.h" #include "CapturingThisInMemberVariableCheck.h" -#include "CastingThroughVoidCheck.h" #include "CastToStructCheck.h" +#include "CastingThroughVoidCheck.h" #include "ChainedComparisonCheck.h" #include "ComparePointerToMemberVirtualFunctionCheck.h" #include "CopyConstructorInitCheck.h" 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 9b9afa43b6a96..5a9d96136298e 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 @@ -38,7 +38,7 @@ Limitations The check does run only on `C` code. -C-style casts are discouraged in `C++` and should be converted to more type-safe +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. @@ -52,7 +52,7 @@ Options 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 + `;` 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 From c14fce880a800f16928fecf0159c9694fc726879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.k...@ericsson.com> Date: Tue, 19 Aug 2025 17:50:44 +0200 Subject: [PATCH 4/5] improved regex matching, updated docs --- .../clang-tidy/bugprone/CastToStructCheck.cpp | 16 +++++++++------- .../clang-tidy/bugprone/CastToStructCheck.h | 1 + .../checks/bugprone/cast-to-struct.rst | 19 +++++++++++++------ .../checkers/bugprone/cast-to-struct-ignore.c | 12 ++++++------ 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp index 9915d32e1ddbd..61730f58b20ae 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp @@ -18,7 +18,11 @@ namespace clang::tidy::bugprone { CastToStructCheck::CastToStructCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), IgnoredCasts( - utils::options::parseStringList(Options.get("IgnoredCasts", ""))) {} + utils::options::parseStringList(Options.get("IgnoredCasts", ""))) { + IgnoredCastsRegex.reserve(IgnoredCasts.size()); + for (const auto &Str : IgnoredCasts) + IgnoredCastsRegex.emplace_back(Str); +} void CastToStructCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "IgnoredCasts", @@ -60,14 +64,12 @@ void CastToStructCheck::check(const MatchFinder::MatchResult &Result) { 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)) { + bool FromMatch = false; + for (auto [Idx, Regex] : llvm::enumerate(IgnoredCastsRegex)) { if (Idx % 2 == 0) { - FromR = llvm::Regex(Str); + FromMatch = Regex.match(FromName); } else { - ToR = llvm::Regex(Str); - if (FromR.match(FromName) && ToR.match(ToName)) + if (FromMatch && Regex.match(ToName)) return; } } diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h index ec89fa8ab6dfc..24ad285ae2a31 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h @@ -30,6 +30,7 @@ class CastToStructCheck : public ClangTidyCheck { private: std::vector<llvm::StringRef> IgnoredCasts; + std::vector<llvm::Regex> IgnoredCastsRegex; }; } // 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 5a9d96136298e..571b231b2d479 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 @@ -52,9 +52,16 @@ Options 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.) + `;` characters. For example `char;struct Type1;char;struct Type2` specifies + that the check does not produce warning for casts from ``char *`` to + ``struct Type1 *`` and casts from ``char *`` to ``struct Type2 *`` (the `*` + character to indicate pointer should not be used in the list). The type name + in the cast expression is matched without resolution of type aliases like + ``typedef``. + + The list entries are matched as substring regular expressions. For example + `char` would match `unsigned char` too. This problem can be avoided by using + anchor characters (`^char$`). + + Default value of the option is an 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 e64fe177448d9..32d4d78265e3f 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,5 +1,5 @@ // RUN: %check_clang_tidy %s bugprone-cast-to-struct %t -- \ -// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredCasts: 'char;S1;int;Other*'}}" +// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredCasts: '^char$;^struct S1$;^int$;Other'}}" struct S1 { int a; @@ -9,7 +9,7 @@ struct S2 { char a; }; -struct OtherS { +struct SomeOtherS { int a; int b; }; @@ -22,10 +22,10 @@ void test1(char *p1, int *p2) { // 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 SomeOtherS *s3; + s3 = (struct SomeOtherS *)p2; + s3 = (struct SomeOtherS *)p1; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct SomeOtherS *' } struct S2 *test_void_is_always_ignored(void *p) { From b13b3667f6e9c77d8a2909db9ffc44f276620b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <balazs.k...@ericsson.com> Date: Wed, 27 Aug 2025 16:25:25 +0200 Subject: [PATCH 5/5] new behavior of ignore list --- .../clang-tidy/bugprone/CastToStructCheck.cpp | 51 ++++++++++----- .../clang-tidy/bugprone/CastToStructCheck.h | 2 +- .../checks/bugprone/cast-to-struct.rst | 29 ++++----- .../checkers/bugprone/cast-to-struct-ignore.c | 64 ++++++++++++++----- .../checkers/bugprone/cast-to-struct.c | 12 ++-- 5 files changed, 102 insertions(+), 56 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp index 61730f58b20ae..0a18ae9b12547 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp @@ -15,13 +15,30 @@ using namespace clang::ast_matchers; namespace clang::tidy::bugprone { +namespace { + +AST_MATCHER(Type, charType) { + return Node.isCharType(); +} +AST_MATCHER(Type, unionType) { + return Node.isUnionType(); +} + +} + CastToStructCheck::CastToStructCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), IgnoredCasts( utils::options::parseStringList(Options.get("IgnoredCasts", ""))) { IgnoredCastsRegex.reserve(IgnoredCasts.size()); - for (const auto &Str : IgnoredCasts) - IgnoredCastsRegex.emplace_back(Str); + for (const auto &Str : IgnoredCasts) { + std::string WholeWordRegex; + WholeWordRegex.reserve(Str.size() + 2); + WholeWordRegex.push_back('^'); + WholeWordRegex.append(Str); + WholeWordRegex.push_back('$'); + IgnoredCastsRegex.emplace_back(WholeWordRegex); + } } void CastToStructCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { @@ -33,7 +50,8 @@ void CastToStructCheck::registerMatchers(MatchFinder *Finder) { auto FromPointee = qualType(hasUnqualifiedDesugaredType(type().bind("FromType")), unless(voidType()), - unless(hasDeclaration(recordDecl(isUnion())))) + unless(charType()), + unless(unionType())) .bind("FromPointee"); auto ToPointee = qualType(hasUnqualifiedDesugaredType( @@ -53,26 +71,27 @@ void CastToStructCheck::check(const MatchFinder::MatchResult &Result) { 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 (FromType == ToType) return; - const std::string FromName = FromPointee->getAsString(); - const std::string ToName = ToPointee->getAsString(); - bool FromMatch = false; - for (auto [Idx, Regex] : llvm::enumerate(IgnoredCastsRegex)) { - if (Idx % 2 == 0) { - FromMatch = Regex.match(FromName); - } else { - if (FromMatch && Regex.match(ToName)) - return; + auto CheckNameIgnore = [this](const std::string &FromName, const std::string &ToName) { + bool FromMatch = false; + for (auto [Idx, Regex] : llvm::enumerate(IgnoredCastsRegex)) { + if (Idx % 2 == 0) { + FromMatch = Regex.match(FromName); + } else { + if (FromMatch && Regex.match(ToName)) + return true; + } } - } + return false; + }; + + if (CheckNameIgnore(FromPtr->getAsString(), ToPtr->getAsString())) + return; diag(FoundCastExpr->getExprLoc(), "casting a %0 pointer to a " diff --git a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h index 24ad285ae2a31..20bc9766c3262 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h @@ -25,7 +25,7 @@ class CastToStructCheck : public ClangTidyCheck { 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; + return !LangOpts.CPlusPlus; } private: 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 571b231b2d479..06fbd8adc4a66 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 @@ -11,16 +11,17 @@ 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 +``malloc``-like functions) and ``char *`` types (which are used often as +pointers into data buffers). 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 - void test1(char *p) { + void test1(int *p) { struct S1 *s; - s = (struct S1 *)p; // warn: 'char *' is converted to 'struct S1 *' + s = (struct S1 *)p; // warn: 'int *' is converted to 'struct S1 *' } void test2(struct S1 *p) { @@ -36,7 +37,7 @@ types. Limitations ----------- -The check does run only on `C` code. +The check does not run on `C++` code. 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 @@ -52,16 +53,12 @@ Options 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;struct Type1;char;struct Type2` specifies - that the check does not produce warning for casts from ``char *`` to - ``struct Type1 *`` and casts from ``char *`` to ``struct Type2 *`` (the `*` - character to indicate pointer should not be used in the list). The type name - in the cast expression is matched without resolution of type aliases like - ``typedef``. + `;` characters. The parts between `;` characters are matched as regular + expressions over the whole type name. For example + `struct S1 \*;struct T1 \*;short \*;struct T1 \*` specifies that the check + does not produce warning for casts from ``struct S1 *`` to ``struct T1 *`` + and casts from ``short *`` to ``struct T1 *`` (the `*` character needs to be + escaped). The type name in the cast expression is matched without resolution + of ``typedef`` types. - The list entries are matched as substring regular expressions. For example - `char` would match `unsigned char` too. This problem can be avoided by using - anchor characters (`^char$`). - - Default value of the option is an empty list. (Casts from ``void *`` are - ignored always regardless of this list.) + Default value of the option is an empty 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 32d4d78265e3f..f1abcf57f2a3e 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,33 +1,63 @@ // RUN: %check_clang_tidy %s bugprone-cast-to-struct %t -- \ -// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredCasts: '^char$;^struct S1$;^int$;Other'}}" +// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredCasts: 'int \*;struct S1 \*;TYPE \*;struct S2 \*;TYPE_P;struct S3 \*;struct S4 \*;struct S. \*'}}" struct S1 { - int a; }; struct S2 { - char a; }; -struct SomeOtherS { - int a; - int b; +struct S3 { }; -void test1(char *p1, int *p2) { +struct S4 { +}; + +typedef int TYPE; +typedef int * TYPE_P; +typedef unsigned char uchar; + +void test1(int *p0, TYPE *p1, TYPE_P p2) { + struct S1 *s1; + struct S4 *s4; + + s1 = (struct S1 *)p0; // no warning + s1 = (struct S1 *)p1; // CHECK-MESSAGES: warning: casting a 'TYPE *' (aka 'int *') pointer to a 'struct S1 *' + s1 = (struct S1 *)p2; // CHECK-MESSAGES: warning: casting a 'TYPE_P' (aka 'int *') pointer to a 'struct S1 *' + s4 = (struct S4 *)p0; // CHECK-MESSAGES: warning: casting a 'int *' pointer to a 'struct S4 *' +} + +void test2(int *p0, TYPE *p1, TYPE_P p2) { + struct S2 *s2; + struct S4 *s4; + + s2 = (struct S2 *)p0; // CHECK-MESSAGES: warning: casting a 'int *' pointer to a 'struct S2 *' + s2 = (struct S2 *)p1; // no warning + s2 = (struct S2 *)p2; // CHECK-MESSAGES: warning: casting a 'TYPE_P' (aka 'int *') pointer to a 'struct S2 *' + s4 = (struct S4 *)p1; // CHECK-MESSAGES: warning: casting a 'TYPE *' (aka 'int *') pointer to a 'struct S4 *' +} + +void test3(int *p0, TYPE *p1, TYPE_P p2) { + struct S3 *s3; + struct S4 *s4; + + s3 = (struct S3 *)p0; // CHECK-MESSAGES: warning: casting a 'int *' pointer to a 'struct S3 *' + s3 = (struct S3 *)p1; // CHECK-MESSAGES: warning: casting a 'TYPE *' (aka 'int *') pointer to a 'struct S3 *' + s3 = (struct S3 *)p2; // no warning + s4 = (struct S4 *)p2; // CHECK-MESSAGES: warning: casting a 'TYPE_P' (aka 'int *') pointer to a 'struct S4 *' +} + +void test_wildcard(struct S4 *p1, struct S1 *p2) { struct S1 *s1; - s1 = (struct S1 *)p1; struct S2 *s2; + s1 = (struct S1 *)p1; 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 SomeOtherS *s3; - s3 = (struct SomeOtherS *)p2; - s3 = (struct SomeOtherS *)p1; - // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct SomeOtherS *' + s2 = (struct S2 *)p2; // CHECK-MESSAGES: warning: casting a 'struct S1 *' pointer to a 'struct S2 *' } -struct S2 *test_void_is_always_ignored(void *p) { - return (struct S2 *)p; +void test_default_ignore(void *p1, char *p2, uchar *p3) { + struct S4 *s4; + s4 = (struct S4 *)p1; + s4 = (struct S4 *)p2; + s4 = (struct S4 *)p3; } 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 index cfabf69351f39..44921aaaf0be7 100644 --- 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 @@ -26,9 +26,9 @@ typedef union U1 *TyPU1; typedef int int_t; typedef int * int_ptr_t; -struct S1 *test_simple(char *p) { +struct S1 *test_simple(short *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] + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: casting a 'short *' 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; @@ -48,18 +48,18 @@ TyPS1 test_cast_from_similar(struct S1 *p) { return (TyPS1)p; } -void test_typedef(char *p1, int_t *p2, int_ptr_t p3) { +void test_typedef(short *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] + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: casting a 'short *' 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] + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: casting a 'short *' 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) { +void test_union(short *p1, union U1 *p2, TyPU1 p3) { union U1 *a = (union U1 *)p1; struct S1 *b = (struct S1 *)p2; struct S1 *c = (struct S1 *)p3; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits