Skip to content

Commit b13b366

Browse files
committed
new behavior of ignore list
1 parent c14fce8 commit b13b366

File tree

5 files changed

+102
-56
lines changed

5 files changed

+102
-56
lines changed

clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.cpp

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,30 @@ using namespace clang::ast_matchers;
1515

1616
namespace clang::tidy::bugprone {
1717

18+
namespace {
19+
20+
AST_MATCHER(Type, charType) {
21+
return Node.isCharType();
22+
}
23+
AST_MATCHER(Type, unionType) {
24+
return Node.isUnionType();
25+
}
26+
27+
}
28+
1829
CastToStructCheck::CastToStructCheck(StringRef Name, ClangTidyContext *Context)
1930
: ClangTidyCheck(Name, Context),
2031
IgnoredCasts(
2132
utils::options::parseStringList(Options.get("IgnoredCasts", ""))) {
2233
IgnoredCastsRegex.reserve(IgnoredCasts.size());
23-
for (const auto &Str : IgnoredCasts)
24-
IgnoredCastsRegex.emplace_back(Str);
34+
for (const auto &Str : IgnoredCasts) {
35+
std::string WholeWordRegex;
36+
WholeWordRegex.reserve(Str.size() + 2);
37+
WholeWordRegex.push_back('^');
38+
WholeWordRegex.append(Str);
39+
WholeWordRegex.push_back('$');
40+
IgnoredCastsRegex.emplace_back(WholeWordRegex);
41+
}
2542
}
2643

2744
void CastToStructCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
@@ -33,7 +50,8 @@ void CastToStructCheck::registerMatchers(MatchFinder *Finder) {
3350
auto FromPointee =
3451
qualType(hasUnqualifiedDesugaredType(type().bind("FromType")),
3552
unless(voidType()),
36-
unless(hasDeclaration(recordDecl(isUnion()))))
53+
unless(charType()),
54+
unless(unionType()))
3755
.bind("FromPointee");
3856
auto ToPointee =
3957
qualType(hasUnqualifiedDesugaredType(
@@ -53,26 +71,27 @@ void CastToStructCheck::check(const MatchFinder::MatchResult &Result) {
5371
Result.Nodes.getNodeAs<CStyleCastExpr>("CastExpr");
5472
const auto *const FromPtr = Result.Nodes.getNodeAs<QualType>("FromPtr");
5573
const auto *const ToPtr = Result.Nodes.getNodeAs<QualType>("ToPtr");
56-
const auto *const FromPointee =
57-
Result.Nodes.getNodeAs<QualType>("FromPointee");
58-
const auto *const ToPointee = Result.Nodes.getNodeAs<QualType>("ToPointee");
5974
const auto *const FromType = Result.Nodes.getNodeAs<Type>("FromType");
6075
const auto *const ToType = Result.Nodes.getNodeAs<RecordType>("ToType");
6176

6277
if (FromType == ToType)
6378
return;
6479

65-
const std::string FromName = FromPointee->getAsString();
66-
const std::string ToName = ToPointee->getAsString();
67-
bool FromMatch = false;
68-
for (auto [Idx, Regex] : llvm::enumerate(IgnoredCastsRegex)) {
69-
if (Idx % 2 == 0) {
70-
FromMatch = Regex.match(FromName);
71-
} else {
72-
if (FromMatch && Regex.match(ToName))
73-
return;
80+
auto CheckNameIgnore = [this](const std::string &FromName, const std::string &ToName) {
81+
bool FromMatch = false;
82+
for (auto [Idx, Regex] : llvm::enumerate(IgnoredCastsRegex)) {
83+
if (Idx % 2 == 0) {
84+
FromMatch = Regex.match(FromName);
85+
} else {
86+
if (FromMatch && Regex.match(ToName))
87+
return true;
88+
}
7489
}
75-
}
90+
return false;
91+
};
92+
93+
if (CheckNameIgnore(FromPtr->getAsString(), ToPtr->getAsString()))
94+
return;
7695

7796
diag(FoundCastExpr->getExprLoc(),
7897
"casting a %0 pointer to a "

clang-tools-extra/clang-tidy/bugprone/CastToStructCheck.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class CastToStructCheck : public ClangTidyCheck {
2525
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
2626
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
2727
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
28-
return LangOpts.C99;
28+
return !LangOpts.CPlusPlus;
2929
}
3030

3131
private:

clang-tools-extra/docs/clang-tidy/checks/bugprone/cast-to-struct.rst

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ scalar-type pointer (which points often to an array or memory block) to a
1111
``struct`` type pointer can be unsafe for similar reasons. This check warns at
1212
pointer casts from any non-struct type to a struct type. No warning is produced
1313
at cast from type ``void *`` (this is the usual way of allocating memory with
14-
``malloc``-like functions). In addition, ``union`` types are excluded from the
14+
``malloc``-like functions) and ``char *`` types (which are used often as
15+
pointers into data buffers). In addition, ``union`` types are excluded from the
1516
check. It is possible to specify additional types to ignore. The check does not
1617
take into account type compatibility or data layout, only the names of the
1718
types.
1819

1920
.. code-block:: c
2021
21-
void test1(char *p) {
22+
void test1(int *p) {
2223
struct S1 *s;
23-
s = (struct S1 *)p; // warn: 'char *' is converted to 'struct S1 *'
24+
s = (struct S1 *)p; // warn: 'int *' is converted to 'struct S1 *'
2425
}
2526
2627
void test2(struct S1 *p) {
@@ -36,7 +37,7 @@ types.
3637
Limitations
3738
-----------
3839

39-
The check does run only on `C` code.
40+
The check does not run on `C++` code.
4041

4142
C-style casts are discouraged in C++ and should be converted to more type-safe
4243
casts. The ``reinterpreted_cast`` is used for the most unsafe cases and
@@ -52,16 +53,12 @@ Options
5253
types to ignore. The list should contain pairs of type names in a way that
5354
the first type is the "from" type, the second is the "to" type in a cast
5455
expression. The types in a pair and the pairs itself are separated by
55-
`;` characters. For example `char;struct Type1;char;struct Type2` specifies
56-
that the check does not produce warning for casts from ``char *`` to
57-
``struct Type1 *`` and casts from ``char *`` to ``struct Type2 *`` (the `*`
58-
character to indicate pointer should not be used in the list). The type name
59-
in the cast expression is matched without resolution of type aliases like
60-
``typedef``.
56+
`;` characters. The parts between `;` characters are matched as regular
57+
expressions over the whole type name. For example
58+
`struct S1 \*;struct T1 \*;short \*;struct T1 \*` specifies that the check
59+
does not produce warning for casts from ``struct S1 *`` to ``struct T1 *``
60+
and casts from ``short *`` to ``struct T1 *`` (the `*` character needs to be
61+
escaped). The type name in the cast expression is matched without resolution
62+
of ``typedef`` types.
6163

62-
The list entries are matched as substring regular expressions. For example
63-
`char` would match `unsigned char` too. This problem can be avoided by using
64-
anchor characters (`^char$`).
65-
66-
Default value of the option is an empty list. (Casts from ``void *`` are
67-
ignored always regardless of this list.)
64+
Default value of the option is an empty list.
Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,63 @@
11
// RUN: %check_clang_tidy %s bugprone-cast-to-struct %t -- \
2-
// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredCasts: '^char$;^struct S1$;^int$;Other'}}"
2+
// RUN: -config="{CheckOptions: {bugprone-cast-to-struct.IgnoredCasts: 'int \*;struct S1 \*;TYPE \*;struct S2 \*;TYPE_P;struct S3 \*;struct S4 \*;struct S. \*'}}"
33

44
struct S1 {
5-
int a;
65
};
76

87
struct S2 {
9-
char a;
108
};
119

12-
struct SomeOtherS {
13-
int a;
14-
int b;
10+
struct S3 {
1511
};
1612

17-
void test1(char *p1, int *p2) {
13+
struct S4 {
14+
};
15+
16+
typedef int TYPE;
17+
typedef int * TYPE_P;
18+
typedef unsigned char uchar;
19+
20+
void test1(int *p0, TYPE *p1, TYPE_P p2) {
21+
struct S1 *s1;
22+
struct S4 *s4;
23+
24+
s1 = (struct S1 *)p0; // no warning
25+
s1 = (struct S1 *)p1; // CHECK-MESSAGES: warning: casting a 'TYPE *' (aka 'int *') pointer to a 'struct S1 *'
26+
s1 = (struct S1 *)p2; // CHECK-MESSAGES: warning: casting a 'TYPE_P' (aka 'int *') pointer to a 'struct S1 *'
27+
s4 = (struct S4 *)p0; // CHECK-MESSAGES: warning: casting a 'int *' pointer to a 'struct S4 *'
28+
}
29+
30+
void test2(int *p0, TYPE *p1, TYPE_P p2) {
31+
struct S2 *s2;
32+
struct S4 *s4;
33+
34+
s2 = (struct S2 *)p0; // CHECK-MESSAGES: warning: casting a 'int *' pointer to a 'struct S2 *'
35+
s2 = (struct S2 *)p1; // no warning
36+
s2 = (struct S2 *)p2; // CHECK-MESSAGES: warning: casting a 'TYPE_P' (aka 'int *') pointer to a 'struct S2 *'
37+
s4 = (struct S4 *)p1; // CHECK-MESSAGES: warning: casting a 'TYPE *' (aka 'int *') pointer to a 'struct S4 *'
38+
}
39+
40+
void test3(int *p0, TYPE *p1, TYPE_P p2) {
41+
struct S3 *s3;
42+
struct S4 *s4;
43+
44+
s3 = (struct S3 *)p0; // CHECK-MESSAGES: warning: casting a 'int *' pointer to a 'struct S3 *'
45+
s3 = (struct S3 *)p1; // CHECK-MESSAGES: warning: casting a 'TYPE *' (aka 'int *') pointer to a 'struct S3 *'
46+
s3 = (struct S3 *)p2; // no warning
47+
s4 = (struct S4 *)p2; // CHECK-MESSAGES: warning: casting a 'TYPE_P' (aka 'int *') pointer to a 'struct S4 *'
48+
}
49+
50+
void test_wildcard(struct S4 *p1, struct S1 *p2) {
1851
struct S1 *s1;
19-
s1 = (struct S1 *)p1;
2052
struct S2 *s2;
53+
s1 = (struct S1 *)p1;
2154
s2 = (struct S2 *)p1;
22-
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct S2 *'
23-
s2 = (struct S2 *)p2;
24-
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'int *' pointer to a 'struct S2 *'
25-
struct SomeOtherS *s3;
26-
s3 = (struct SomeOtherS *)p2;
27-
s3 = (struct SomeOtherS *)p1;
28-
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: casting a 'char *' pointer to a 'struct SomeOtherS *'
55+
s2 = (struct S2 *)p2; // CHECK-MESSAGES: warning: casting a 'struct S1 *' pointer to a 'struct S2 *'
2956
}
3057

31-
struct S2 *test_void_is_always_ignored(void *p) {
32-
return (struct S2 *)p;
58+
void test_default_ignore(void *p1, char *p2, uchar *p3) {
59+
struct S4 *s4;
60+
s4 = (struct S4 *)p1;
61+
s4 = (struct S4 *)p2;
62+
s4 = (struct S4 *)p3;
3363
}

clang-tools-extra/test/clang-tidy/checkers/bugprone/cast-to-struct.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ typedef union U1 *TyPU1;
2626
typedef int int_t;
2727
typedef int * int_ptr_t;
2828

29-
struct S1 *test_simple(char *p) {
29+
struct S1 *test_simple(short *p) {
3030
return (struct S1 *)p;
31-
// 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]
31+
// 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]
3232
struct S1 *s;
3333
int i;
3434
s = (struct S1 *)&i;
@@ -48,18 +48,18 @@ TyPS1 test_cast_from_similar(struct S1 *p) {
4848
return (TyPS1)p;
4949
}
5050

51-
void test_typedef(char *p1, int_t *p2, int_ptr_t p3) {
51+
void test_typedef(short *p1, int_t *p2, int_ptr_t p3) {
5252
TyS1 *a = (TyS1 *)p1;
53-
// 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]
53+
// 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]
5454
TyPS1 b = (TyPS1)p1;
55-
// 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]
55+
// 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]
5656
struct S1 *c = (struct S1 *)p2;
5757
// 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]
5858
struct S1 *d = (struct S1 *)p3;
5959
// 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]
6060
}
6161

62-
void test_union(char *p1, union U1 *p2, TyPU1 p3) {
62+
void test_union(short *p1, union U1 *p2, TyPU1 p3) {
6363
union U1 *a = (union U1 *)p1;
6464
struct S1 *b = (struct S1 *)p2;
6565
struct S1 *c = (struct S1 *)p3;

0 commit comments

Comments
 (0)