Skip to content

Commit af07d7b

Browse files
authored
[clang-tidy] Improved cppcoreguidelines-pro-type-const-cast (#69501)
Improved cppcoreguidelines-pro-type-const-cast check to ignore casts to const type (controlled by option) and casts in implicitly invoked code. Fixes #69319
1 parent 1097c71 commit af07d7b

File tree

6 files changed

+174
-12
lines changed

6 files changed

+174
-12
lines changed

clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,60 @@ using namespace clang::ast_matchers;
1414

1515
namespace clang::tidy::cppcoreguidelines {
1616

17+
static bool hasConstQualifier(QualType Type) {
18+
const QualType PtrType = Type->getPointeeType();
19+
if (!PtrType.isNull())
20+
return hasConstQualifier(PtrType);
21+
22+
return Type.isConstQualified();
23+
}
24+
25+
static bool hasVolatileQualifier(QualType Type) {
26+
const QualType PtrType = Type->getPointeeType();
27+
if (!PtrType.isNull())
28+
return hasVolatileQualifier(PtrType);
29+
return Type.isVolatileQualified();
30+
}
31+
32+
ProTypeConstCastCheck::ProTypeConstCastCheck(StringRef Name,
33+
ClangTidyContext *Context)
34+
: ClangTidyCheck(Name, Context),
35+
StrictMode(Options.getLocalOrGlobal("StrictMode", false)) {}
36+
37+
void ProTypeConstCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
38+
Options.store(Opts, "StrictMode", StrictMode);
39+
}
40+
1741
void ProTypeConstCastCheck::registerMatchers(MatchFinder *Finder) {
1842
Finder->addMatcher(cxxConstCastExpr().bind("cast"), this);
1943
}
2044

2145
void ProTypeConstCastCheck::check(const MatchFinder::MatchResult &Result) {
2246
const auto *MatchedCast = Result.Nodes.getNodeAs<CXXConstCastExpr>("cast");
23-
diag(MatchedCast->getOperatorLoc(), "do not use const_cast");
47+
if (StrictMode) {
48+
diag(MatchedCast->getOperatorLoc(), "do not use const_cast");
49+
return;
50+
}
51+
52+
const QualType TargetType = MatchedCast->getType().getCanonicalType();
53+
const QualType SourceType =
54+
MatchedCast->getSubExpr()->getType().getCanonicalType();
55+
56+
const bool RemovingConst =
57+
hasConstQualifier(SourceType) && !hasConstQualifier(TargetType);
58+
const bool RemovingVolatile =
59+
hasVolatileQualifier(SourceType) && !hasVolatileQualifier(TargetType);
60+
61+
if (!RemovingConst && !RemovingVolatile) {
62+
// Cast is doing nothing.
63+
return;
64+
}
65+
66+
diag(MatchedCast->getOperatorLoc(),
67+
"do not use const_cast to remove%select{| const}0%select{| "
68+
"and}2%select{| volatile}1 qualifier")
69+
<< RemovingConst << RemovingVolatile
70+
<< (RemovingConst && RemovingVolatile);
2471
}
2572

2673
} // namespace clang::tidy::cppcoreguidelines

clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,25 @@
1313

1414
namespace clang::tidy::cppcoreguidelines {
1515

16-
/// This check flags all instances of const_cast
16+
/// Imposes limitations on the use of const_cast within C++ code.
1717
///
1818
/// For the user-facing documentation see:
1919
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-type-const-cast.html
2020
class ProTypeConstCastCheck : public ClangTidyCheck {
2121
public:
22-
ProTypeConstCastCheck(StringRef Name, ClangTidyContext *Context)
23-
: ClangTidyCheck(Name, Context) {}
22+
ProTypeConstCastCheck(StringRef Name, ClangTidyContext *Context);
2423
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
2524
return LangOpts.CPlusPlus;
2625
}
2726
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
2827
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
28+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
29+
std::optional<TraversalKind> getCheckTraversalKind() const override {
30+
return TK_IgnoreUnlessSpelledInSource;
31+
}
32+
33+
private:
34+
const bool StrictMode;
2935
};
3036

3137
} // namespace clang::tidy::cppcoreguidelines

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ Changes in existing checks
255255
<clang-tidy/checks/cppcoreguidelines/pro-bounds-constant-array-index>` check
256256
to perform checks on derived classes of ``std::array``.
257257

258+
- Improved :doc:`cppcoreguidelines-pro-type-const-cast
259+
<clang-tidy/checks/cppcoreguidelines/pro-type-const-cast>` check to ignore
260+
casts to ``const`` or ``volatile`` type (controlled by `StrictMode` option)
261+
and casts in implicitly invoked code.
262+
258263
- Improved :doc:`cppcoreguidelines-pro-type-member-init
259264
<clang-tidy/checks/cppcoreguidelines/pro-type-member-init>` check to ignore
260265
dependent delegate constructors.

clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/pro-type-const-cast.rst

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,35 @@
33
cppcoreguidelines-pro-type-const-cast
44
=====================================
55

6-
This check flags all uses of ``const_cast`` in C++ code.
6+
Imposes limitations on the use of ``const_cast`` within C++ code. It depends on
7+
the :option:`StrictMode` option setting to determine whether it should flag all
8+
instances of ``const_cast`` or only those that remove either ``const`` or
9+
``volatile`` qualifier.
710

8-
Modifying a variable that was declared const is undefined behavior, even with
9-
``const_cast``.
11+
Modifying a variable that has been declared as ``const`` in C++ is generally
12+
considered undefined behavior, and this remains true even when using
13+
``const_cast``. In C++, the ``const`` qualifier indicates that a variable is
14+
intended to be read-only, and the compiler enforces this by disallowing any
15+
attempts to change the value of that variable.
16+
17+
Removing the ``volatile`` qualifier in C++ can have serious consequences. This
18+
qualifier indicates that a variable's value can change unpredictably, and
19+
removing it may lead to undefined behavior, optimization problems, and debugging
20+
challenges. It's essential to retain the ``volatile`` qualifier in situations
21+
where the variable's volatility is a crucial aspect of program correctness and
22+
reliability.
1023

1124
This rule is part of the `Type safety (Type 3)
1225
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Pro-type-constcast>`_
13-
profile from the C++ Core Guidelines.
26+
profile and `ES.50: Don’t cast away const
27+
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es50-dont-cast-away-const>`_
28+
rule from the C++ Core Guidelines.
29+
30+
Options
31+
-------
32+
33+
.. option:: StrictMode
34+
35+
When this setting is set to `true`, it means that any usage of ``const_cast``
36+
is not allowed. On the other hand, when it's set to `false`, it permits
37+
casting to ``const`` or ``volatile`` types. Default value is `false`.
Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,86 @@
1-
// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-const-cast %t
1+
// RUN: %check_clang_tidy -check-suffix=STRICT %s cppcoreguidelines-pro-type-const-cast %t -- -config="{CheckOptions: {StrictMode: true}}"
2+
// RUN: %check_clang_tidy -check-suffix=NSTRICT %s cppcoreguidelines-pro-type-const-cast %t
23

4+
namespace Const {
35
const int *i;
46
int *j;
5-
void f() { j = const_cast<int *>(i); }
6-
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
7+
8+
void f() {
9+
j = const_cast<int *>(i);
10+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:7: warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast]
11+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
12+
13+
i = const_cast<const int*>(j);
14+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
15+
16+
j = *const_cast<int **>(&i);
17+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast]
18+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
19+
20+
i = *const_cast<const int**>(&j);
21+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
22+
23+
j = &const_cast<int&>(*i);
24+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast]
25+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
26+
27+
i = &const_cast<const int&>(*j);
28+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
29+
}
30+
}
31+
32+
namespace Volatile {
33+
volatile int *i;
34+
int *j;
35+
36+
void f() {
37+
j = const_cast<int *>(i);
38+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:7: warning: do not use const_cast to remove volatile qualifier [cppcoreguidelines-pro-type-const-cast]
39+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
40+
41+
i = const_cast<volatile int*>(j);
42+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
43+
44+
j = *const_cast<int **>(&i);
45+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove volatile qualifier [cppcoreguidelines-pro-type-const-cast]
46+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
47+
48+
i = *const_cast<volatile int**>(&j);
49+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
50+
51+
j = &const_cast<int&>(*i);
52+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove volatile qualifier [cppcoreguidelines-pro-type-const-cast]
53+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
54+
55+
i = &const_cast<volatile int&>(*j);
56+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
57+
}
58+
}
59+
60+
namespace ConstAndVolatile {
61+
const volatile int *i;
62+
int *j;
63+
64+
void f() {
65+
j = const_cast<int *>(i);
66+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:7: warning: do not use const_cast to remove const and volatile qualifier [cppcoreguidelines-pro-type-const-cast]
67+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
68+
69+
i = const_cast<const volatile int*>(j);
70+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
71+
72+
j = *const_cast<int **>(&i);
73+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove const and volatile qualifier [cppcoreguidelines-pro-type-const-cast]
74+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
75+
76+
i = *const_cast<const volatile int**>(&j);
77+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
78+
79+
j = &const_cast<int&>(*i);
80+
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove const and volatile qualifier [cppcoreguidelines-pro-type-const-cast]
81+
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
82+
83+
i = &const_cast<const volatile int&>(*j);
84+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
85+
}
86+
}

clang-tools-extra/test/clang-tidy/infrastructure/nonstandard-file-extension.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
const int *i;
44
int *j;
55
void f() { j = const_cast<int *>(i); }
6-
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
6+
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast]

0 commit comments

Comments
 (0)