Skip to content

Commit

Permalink
[clang-tidy] Improved cppcoreguidelines-pro-type-const-cast (llvm#69501)
Browse files Browse the repository at this point in the history
Improved cppcoreguidelines-pro-type-const-cast check to ignore casts to
const type (controlled by option) and casts in implicitly invoked code.

Fixes llvm#69319
  • Loading branch information
PiotrZSL authored and zahiraam committed Oct 26, 2023
1 parent bf8922b commit d7228ec
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,60 @@ using namespace clang::ast_matchers;

namespace clang::tidy::cppcoreguidelines {

static bool hasConstQualifier(QualType Type) {
const QualType PtrType = Type->getPointeeType();
if (!PtrType.isNull())
return hasConstQualifier(PtrType);

return Type.isConstQualified();
}

static bool hasVolatileQualifier(QualType Type) {
const QualType PtrType = Type->getPointeeType();
if (!PtrType.isNull())
return hasVolatileQualifier(PtrType);
return Type.isVolatileQualified();
}

ProTypeConstCastCheck::ProTypeConstCastCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
StrictMode(Options.getLocalOrGlobal("StrictMode", false)) {}

void ProTypeConstCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "StrictMode", StrictMode);
}

void ProTypeConstCastCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(cxxConstCastExpr().bind("cast"), this);
}

void ProTypeConstCastCheck::check(const MatchFinder::MatchResult &Result) {
const auto *MatchedCast = Result.Nodes.getNodeAs<CXXConstCastExpr>("cast");
diag(MatchedCast->getOperatorLoc(), "do not use const_cast");
if (StrictMode) {
diag(MatchedCast->getOperatorLoc(), "do not use const_cast");
return;
}

const QualType TargetType = MatchedCast->getType().getCanonicalType();
const QualType SourceType =
MatchedCast->getSubExpr()->getType().getCanonicalType();

const bool RemovingConst =
hasConstQualifier(SourceType) && !hasConstQualifier(TargetType);
const bool RemovingVolatile =
hasVolatileQualifier(SourceType) && !hasVolatileQualifier(TargetType);

if (!RemovingConst && !RemovingVolatile) {
// Cast is doing nothing.
return;
}

diag(MatchedCast->getOperatorLoc(),
"do not use const_cast to remove%select{| const}0%select{| "
"and}2%select{| volatile}1 qualifier")
<< RemovingConst << RemovingVolatile
<< (RemovingConst && RemovingVolatile);
}

} // namespace clang::tidy::cppcoreguidelines
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,25 @@

namespace clang::tidy::cppcoreguidelines {

/// This check flags all instances of const_cast
/// Imposes limitations on the use of const_cast within C++ code.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-type-const-cast.html
class ProTypeConstCastCheck : public ClangTidyCheck {
public:
ProTypeConstCastCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
ProTypeConstCastCheck(StringRef Name, ClangTidyContext *Context);
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}

private:
const bool StrictMode;
};

} // namespace clang::tidy::cppcoreguidelines
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ Changes in existing checks
<clang-tidy/checks/cppcoreguidelines/pro-bounds-constant-array-index>` check
to perform checks on derived classes of ``std::array``.

- Improved :doc:`cppcoreguidelines-pro-type-const-cast
<clang-tidy/checks/cppcoreguidelines/pro-type-const-cast>` check to ignore
casts to ``const`` or ``volatile`` type (controlled by `StrictMode` option)
and casts in implicitly invoked code.

- Improved :doc:`cppcoreguidelines-pro-type-member-init
<clang-tidy/checks/cppcoreguidelines/pro-type-member-init>` check to ignore
dependent delegate constructors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,35 @@
cppcoreguidelines-pro-type-const-cast
=====================================

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

Modifying a variable that was declared const is undefined behavior, even with
``const_cast``.
Modifying a variable that has been declared as ``const`` in C++ is generally
considered undefined behavior, and this remains true even when using
``const_cast``. In C++, the ``const`` qualifier indicates that a variable is
intended to be read-only, and the compiler enforces this by disallowing any
attempts to change the value of that variable.

Removing the ``volatile`` qualifier in C++ can have serious consequences. This
qualifier indicates that a variable's value can change unpredictably, and
removing it may lead to undefined behavior, optimization problems, and debugging
challenges. It's essential to retain the ``volatile`` qualifier in situations
where the variable's volatility is a crucial aspect of program correctness and
reliability.

This rule is part of the `Type safety (Type 3)
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Pro-type-constcast>`_
profile from the C++ Core Guidelines.
profile and `ES.50: Don’t cast away const
<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es50-dont-cast-away-const>`_
rule from the C++ Core Guidelines.

Options
-------

.. option:: StrictMode

When this setting is set to `true`, it means that any usage of ``const_cast``
is not allowed. On the other hand, when it's set to `false`, it permits
casting to ``const`` or ``volatile`` types. Default value is `false`.
Original file line number Diff line number Diff line change
@@ -1,6 +1,86 @@
// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-const-cast %t
// RUN: %check_clang_tidy -check-suffix=STRICT %s cppcoreguidelines-pro-type-const-cast %t -- -config="{CheckOptions: {StrictMode: true}}"
// RUN: %check_clang_tidy -check-suffix=NSTRICT %s cppcoreguidelines-pro-type-const-cast %t

namespace Const {
const int *i;
int *j;
void f() { j = const_cast<int *>(i); }
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

void f() {
j = const_cast<int *>(i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:7: warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = const_cast<const int*>(j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

j = *const_cast<int **>(&i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = *const_cast<const int**>(&j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

j = &const_cast<int&>(*i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = &const_cast<const int&>(*j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
}
}

namespace Volatile {
volatile int *i;
int *j;

void f() {
j = const_cast<int *>(i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:7: warning: do not use const_cast to remove volatile qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = const_cast<volatile int*>(j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

j = *const_cast<int **>(&i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove volatile qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = *const_cast<volatile int**>(&j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

j = &const_cast<int&>(*i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove volatile qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = &const_cast<volatile int&>(*j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
}
}

namespace ConstAndVolatile {
const volatile int *i;
int *j;

void f() {
j = const_cast<int *>(i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:7: warning: do not use const_cast to remove const and volatile qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = const_cast<const volatile int*>(j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:7: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

j = *const_cast<int **>(&i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove const and volatile qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = *const_cast<const volatile int**>(&j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

j = &const_cast<int&>(*i);
// CHECK-MESSAGES-NSTRICT: :[[@LINE-1]]:8: warning: do not use const_cast to remove const and volatile qualifier [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES-STRICT: :[[@LINE-2]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

i = &const_cast<const volatile int&>(*j);
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:8: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
const int *i;
int *j;
void f() { j = const_cast<int *>(i); }
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use const_cast to remove const qualifier [cppcoreguidelines-pro-type-const-cast]

0 comments on commit d7228ec

Please sign in to comment.