llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-tools-extra Author: Lucas Ly Ba (lucasly-ba) <details> <summary>Changes</summary> Replaces a static_cast that only adds const to an lvalue with a call to std::as_const (C++17), which states the intent more clearly and cannot accidentally change the referenced type. The fix also inserts the <utility> include. Fixes #<!-- -->189665 --- Full diff: https://github.com/llvm/llvm-project/pull/210554.diff 8 Files Affected: - (modified) clang-tools-extra/clang-tidy/modernize/CMakeLists.txt (+1) - (modified) clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp (+3) - (added) clang-tools-extra/clang-tidy/modernize/UseAsConstCheck.cpp (+77) - (added) clang-tools-extra/clang-tidy/modernize/UseAsConstCheck.h (+43) - (modified) clang-tools-extra/docs/ReleaseNotes.rst (+6) - (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1) - (added) clang-tools-extra/docs/clang-tidy/checks/modernize/use-as-const.rst (+29) - (added) clang-tools-extra/test/clang-tidy/checkers/modernize/use-as-const.cpp (+19) ``````````diff diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 2c5c44db587fe..78e9c02b16255 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -31,6 +31,7 @@ add_clang_library(clangTidyModernizeModule STATIC ShrinkToFitCheck.cpp TypeTraitsCheck.cpp UnaryStaticAssertCheck.cpp + UseAsConstCheck.cpp UseAutoCheck.cpp UseBoolLiteralsCheck.cpp UseConstraintsCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index cc13da7535bcb..abeff9e72e117 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -31,6 +31,7 @@ #include "ShrinkToFitCheck.h" #include "TypeTraitsCheck.h" #include "UnaryStaticAssertCheck.h" +#include "UseAsConstCheck.h" #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" #include "UseConstraintsCheck.h" @@ -88,6 +89,8 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck<MinMaxUseInitializerListCheck>( "modernize-min-max-use-initializer-list"); CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value"); + CheckFactories.registerCheck<UseAsConstCheck>( + "modernize-use-as-const"); CheckFactories.registerCheck<UseDesignatedInitializersCheck>( "modernize-use-designated-initializers"); CheckFactories.registerCheck<UseIntegerSignComparisonCheck>( diff --git a/clang-tools-extra/clang-tidy/modernize/UseAsConstCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseAsConstCheck.cpp new file mode 100644 index 0000000000000..8fb7a36d904e5 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseAsConstCheck.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 "UseAsConstCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +UseAsConstCheck::UseAsConstCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + Inserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()) {} + +void UseAsConstCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", Inserter.getStyle()); +} + +void UseAsConstCheck::registerMatchers(MatchFinder *Finder) { + // The result of a cast to 'const T &' is a const-qualified lvalue. + Finder->addMatcher( + cxxStaticCastExpr(hasType(qualType(isConstQualified()))).bind("cast"), + this); +} + +void UseAsConstCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + Inserter.registerPreprocessor(PP); +} + +void UseAsConstCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Cast = Result.Nodes.getNodeAs<CXXStaticCastExpr>("cast"); + if (Cast->getBeginLoc().isMacroID()) + return; + + // The cast must be written as 'const T &'. + const auto *RefType = Cast->getTypeAsWritten()->getAs<LValueReferenceType>(); + if (!RefType || !RefType->getPointeeType().isConstQualified()) + return; + + const Expr *Sub = Cast->getSubExprAsWritten(); + if (!Sub->isLValue()) + return; + + // Only rewrite when the cast just adds 'const' to the same type; that is + // exactly what std::as_const does. + ASTContext &Ctx = *Result.Context; + QualType SubType = Sub->getType(); + if (SubType.isConstQualified() || + !Ctx.hasSameUnqualifiedType(SubType, RefType->getPointeeType())) + return; + + const SourceManager &SM = *Result.SourceManager; + StringRef SubText = Lexer::getSourceText( + CharSourceRange::getTokenRange(Sub->getSourceRange()), SM, getLangOpts()); + if (SubText.empty()) + return; + + diag(Cast->getBeginLoc(), + "use 'std::as_const' instead of 'static_cast' to add 'const'") + << FixItHint::CreateReplacement( + Cast->getSourceRange(), ("std::as_const(" + SubText + ")").str()) + << Inserter.createIncludeInsertion(SM.getFileID(Cast->getBeginLoc()), + "<utility>"); +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/UseAsConstCheck.h b/clang-tools-extra/clang-tidy/modernize/UseAsConstCheck.h new file mode 100644 index 0000000000000..145875df06cac --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/UseAsConstCheck.h @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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_MODERNIZE_USEASCONSTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEASCONSTCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" + +namespace clang::tidy::modernize { + +/// Suggests ``std::as_const`` for a ``static_cast`` that only adds ``const`` to +/// an lvalue, e.g. ``static_cast<const T &>(x)`` becomes ``std::as_const(x)``. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-as-const.html +class UseAsConstCheck : public ClangTidyCheck { +public: + UseAsConstCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus17; + } + std::optional<TraversalKind> getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + utils::IncludeInserter Inserter; +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEASCONSTCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 69c3bcf67b8db..243b956b5c2fe 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -97,6 +97,12 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`modernize-use-as-const + <clang-tidy/checks/modernize/use-as-const>` check. + + Replaces a ``static_cast`` that only adds ``const`` to an lvalue with a call + to ``std::as_const``. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 2a44dc78fbc89..24613445f3205 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -312,6 +312,7 @@ Clang-Tidy Checks :doc:`modernize-shrink-to-fit <modernize/shrink-to-fit>`, "Yes" :doc:`modernize-type-traits <modernize/type-traits>`, "Yes" :doc:`modernize-unary-static-assert <modernize/unary-static-assert>`, "Yes" + :doc:`modernize-use-as-const <modernize/use-as-const>`, "Yes" :doc:`modernize-use-auto <modernize/use-auto>`, "Yes" :doc:`modernize-use-bool-literals <modernize/use-bool-literals>`, "Yes" :doc:`modernize-use-constraints <modernize/use-constraints>`, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-as-const.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-as-const.rst new file mode 100644 index 0000000000000..bd75a49c7b431 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-as-const.rst @@ -0,0 +1,29 @@ +.. title:: clang-tidy - modernize-use-as-const + +modernize-use-as-const +====================== + +Replaces a ``static_cast`` that only adds ``const`` to an lvalue with a call to +``std::as_const`` (available since C++17), which states the intent more clearly +and cannot accidentally change the referenced type. + +.. code-block:: c++ + + void use(const std::string &); + + void f(std::string s) { + use(static_cast<const std::string &>(s)); + } + +becomes + +.. code-block:: c++ + + void use(const std::string &); + + void f(std::string s) { + use(std::as_const(s)); + } + +Casts of an already ``const`` operand, and casts that change the type rather than +only adding ``const``, are left untouched. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-as-const.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-as-const.cpp new file mode 100644 index 0000000000000..d1de8e765f073 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-as-const.cpp @@ -0,0 +1,19 @@ +// RUN: %check_clang_tidy -std=c++17 %s modernize-use-as-const %t + +// CHECK-FIXES: #include <utility> + +struct S {}; +void use(const S &); + +void positive(S obj) { + use(static_cast<const S &>(obj)); + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::as_const' instead of 'static_cast' to add 'const' [modernize-use-as-const] + // CHECK-FIXES: use(std::as_const(obj)); +} + +void negative(const S cobj, S obj) { + // Already const: nothing to add. + use(static_cast<const S &>(cobj)); + // Casts to a different type are not just adding const. + use(obj); +} `````````` </details> https://github.com/llvm/llvm-project/pull/210554 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
