diff --git a/libcxx/test/libcxx/clang_tidy.gen.py b/libcxx/test/libcxx/clang_tidy.gen.py index f1135749febe4..dbab2875e3126 100644 --- a/libcxx/test/libcxx/clang_tidy.gen.py +++ b/libcxx/test/libcxx/clang_tidy.gen.py @@ -6,6 +6,7 @@ # # ===----------------------------------------------------------------------===## + # Run our custom libc++ clang-tidy checks on all public headers. # RUN: %{python} %s %{libcxx-dir}/utils @@ -23,6 +24,9 @@ // REQUIRES: has-clang-tidy +// The frozen headers should not be updated to the latest libc++ style, so don't test. +// UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME + // The GCC compiler flags are not always compatible with clang-tidy. // UNSUPPORTED: gcc diff --git a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt index 5797a32974820..521a60c0fc498 100644 --- a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt +++ b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt @@ -96,6 +96,7 @@ set(SOURCES nodebug_on_aliases.cpp proper_version_checks.cpp robust_against_adl.cpp + robust_against_operator_ampersand.cpp uglify_attributes.cpp libcpp_module.cpp diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp index 32a33dddad632..2cf39e2b626f8 100644 --- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp +++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp @@ -16,6 +16,7 @@ #include "nodebug_on_aliases.hpp" #include "proper_version_checks.hpp" #include "robust_against_adl.hpp" +#include "robust_against_operator_ampersand.hpp" #include "uglify_attributes.hpp" namespace { @@ -29,6 +30,8 @@ class LibcxxTestModule : public clang::tidy::ClangTidyModule { check_factories.registerCheck("libcpp-nodebug-on-aliases"); check_factories.registerCheck("libcpp-cpp-version-check"); check_factories.registerCheck("libcpp-robust-against-adl"); + check_factories.registerCheck( + "libcpp-robust-against-operator-ampersand"); check_factories.registerCheck("libcpp-uglify-attributes"); } }; diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp new file mode 100644 index 0000000000000..a608954aa2cc7 --- /dev/null +++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 "clang-tidy/ClangTidyCheck.h" +#include "clang-tidy/ClangTidyModuleRegistry.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/FixIt.h" + +#include "robust_against_operator_ampersand.hpp" + +// This clang-tidy check ensures that we don't use operator& on dependant +// types. If the type is user supplied it may call the type's operator&. +// Instead use std::addressof. +// +// This is part of libc++'s policy +// https://libcxx.llvm.org/CodingGuidelines.html#don-t-use-argument-dependent-lookup-unless-required-by-the-standard + +// TODO(LLVM-21) Remove dependentScopeDeclRefExpr +// dependentScopeDeclRefExpr requires Clang 20, this uses the same definition as Clang +#if defined(__clang_major__) && __clang_major__ < 20 +namespace clang::ast_matchers { +const internal::VariadicDynCastAllOfMatcher dependentScopeDeclRefExpr; +} // namespace clang::ast_matchers +#endif + +namespace libcpp { +robust_against_operator_ampersand::robust_against_operator_ampersand( + llvm::StringRef name, clang::tidy::ClangTidyContext* context) + : clang::tidy::ClangTidyCheck(name, context) {} + +void robust_against_operator_ampersand::registerMatchers(clang::ast_matchers::MatchFinder* finder) { + using namespace clang::ast_matchers; + finder->addMatcher( + cxxOperatorCallExpr(allOf(hasOperatorName("&"), argumentCountIs(1), isTypeDependent()), + unless(hasUnaryOperand(dependentScopeDeclRefExpr()))) + .bind("match"), + this); +} + +void robust_against_operator_ampersand::check(const clang::ast_matchers::MatchFinder::MatchResult& result) { + if (const auto* call = result.Nodes.getNodeAs< clang::CXXOperatorCallExpr >("match"); call != nullptr) { + diag(call->getBeginLoc(), "Guard against user provided operator& for dependent types.") + << clang::FixItHint::CreateReplacement( + call->getSourceRange(), + (llvm::Twine( + "std::addressof(" + clang::tooling::fixit::getText(*call->getArg(0), *result.Context) + ")")) + .str()); + } +} + +} // namespace libcpp diff --git a/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.hpp b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.hpp new file mode 100644 index 0000000000000..5cdc0baca5c23 --- /dev/null +++ b/libcxx/test/tools/clang_tidy_checks/robust_against_operator_ampersand.hpp @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// 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 "clang-tidy/ClangTidyCheck.h" + +namespace libcpp { +class robust_against_operator_ampersand : public clang::tidy::ClangTidyCheck { +public: + robust_against_operator_ampersand(llvm::StringRef, clang::tidy::ClangTidyContext*); + void registerMatchers(clang::ast_matchers::MatchFinder*) override; + void check(const clang::ast_matchers::MatchFinder::MatchResult&) override; +}; +} // namespace libcpp