diff --git a/libcxx/include/__chrono/concepts.h b/libcxx/include/__chrono/concepts.h --- a/libcxx/include/__chrono/concepts.h +++ b/libcxx/include/__chrono/concepts.h @@ -20,12 +20,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD -#if _LIBCPP_STD_VER > 17 +#if _LIBCPP_STD_VER >= 20 template concept __is_hh_mm_ss = __is_specialization_v<_Tp, chrono::hh_mm_ss>; -#endif // _LIBCPP_STD_VER > 17 +#endif // _LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -50,6 +50,7 @@ # define _LIBCPP_FREESTANDING # endif +// NOLINTBEGIN(libcpp-cpp-version-check) # ifndef _LIBCPP_STD_VER # if __cplusplus <= 201103L # define _LIBCPP_STD_VER 11 @@ -64,6 +65,7 @@ # define _LIBCPP_STD_VER 23 # endif # endif // _LIBCPP_STD_VER +// NOLINTEND(libcpp-cpp-version-check) # if defined(__ELF__) # define _LIBCPP_OBJECT_FORMAT_ELF 1 @@ -181,6 +183,7 @@ # define _LIBCPP_TOSTRING2(x) #x # define _LIBCPP_TOSTRING(x) _LIBCPP_TOSTRING2(x) +// NOLINTNEXTLINE(libcpp-cpp-version-check) # if __cplusplus < 201103L # define _LIBCPP_CXX03_LANG # endif diff --git a/libcxx/include/iomanip b/libcxx/include/iomanip --- a/libcxx/include/iomanip +++ b/libcxx/include/iomanip @@ -513,8 +513,6 @@ return __iom_t10<_CharT>(__tm, __fmt); } -#if _LIBCPP_STD_VER >= 11 - template _LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>& __quoted_output(basic_ostream<_CharT, _Traits>& __os, @@ -622,8 +620,6 @@ return __quoted_proxy<_CharT, _Traits, _Allocator>(__s, __delim, __escape); } -#endif // _LIBCPP_STD_VER >= 11 - #if _LIBCPP_STD_VER >= 14 template diff --git a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt --- a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt +++ b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt @@ -17,6 +17,7 @@ set(SOURCES abi_tag_on_virtual.cpp hide_from_abi.cpp + proper_version_checks.cpp qualify_declval.cpp robust_against_adl.cpp uglify_attributes.cpp diff --git a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp --- a/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp +++ b/libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp @@ -11,8 +11,9 @@ #include "abi_tag_on_virtual.hpp" #include "hide_from_abi.hpp" -#include "robust_against_adl.hpp" +#include "proper_version_checks.hpp" #include "qualify_declval.hpp" +#include "robust_against_adl.hpp" #include "uglify_attributes.hpp" namespace { @@ -21,6 +22,7 @@ void addCheckFactories(clang::tidy::ClangTidyCheckFactories& check_factories) override { check_factories.registerCheck("libcpp-avoid-abi-tag-on-virtual"); check_factories.registerCheck("libcpp-hide-from-abi"); + check_factories.registerCheck("libcpp-cpp-version-check"); check_factories.registerCheck("libcpp-robust-against-adl"); check_factories.registerCheck("libcpp-uglify-attributes"); check_factories.registerCheck("libcpp-qualify-declval"); diff --git a/libcxx/test/tools/clang_tidy_checks/proper_version_checks.hpp b/libcxx/test/tools/clang_tidy_checks/proper_version_checks.hpp new file mode 100644 --- /dev/null +++ b/libcxx/test/tools/clang_tidy_checks/proper_version_checks.hpp @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// 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 proper_version_checks : public clang::tidy::ClangTidyCheck { +public: + proper_version_checks(llvm::StringRef, clang::tidy::ClangTidyContext*); + void registerPPCallbacks(const clang::SourceManager& source_manager, + clang::Preprocessor* preprocessor, + clang::Preprocessor* module_expander) override; +}; +} // namespace libcpp diff --git a/libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp b/libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// 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 "proper_version_checks.hpp" + +#include +#include +#include + +namespace libcpp { +namespace { +class proper_version_checks_callbacks : public clang::PPCallbacks { +public: + proper_version_checks_callbacks(clang::Preprocessor& preprocessor, clang::tidy::ClangTidyCheck& check) + : preprocessor_(preprocessor), check_(check) {} + + void If(clang::SourceLocation location, clang::SourceRange condition_range, ConditionValueKind) override { + check_condition(location, condition_range); + } + + void Elif(clang::SourceLocation location, + clang::SourceRange condition_range, + ConditionValueKind, + clang::SourceLocation if_location) override { + check_condition(location, condition_range); + } + +private: + void check_condition(clang::SourceLocation location, clang::SourceRange condition_range) { + std::string_view condition = clang::Lexer::getSourceText( + clang::CharSourceRange::getTokenRange(condition_range), + preprocessor_.getSourceManager(), + preprocessor_.getLangOpts()); + if (preprocessor_.getSourceManager().isInMainFile(location)) + return; + + if (condition.starts_with("_LIBCPP_STD_VER") && condition.find(">") != std::string_view::npos && + condition.find(">=") == std::string_view::npos) + check_.diag(location, "_LIBCPP_STD_VER >= version should be used instead of _LIBCPP_STD_VER > prev_version"); + + else if (condition.starts_with("__cplusplus")) + check_.diag(location, "Use _LIBCPP_STD_VER instead of __cplusplus to constrain based on the C++ version"); + + else if (condition == "_LIBCPP_STD_VER >= 11") + check_.diag(location, "_LIBCPP_STD_VER >= 11 is always true. Did you mean '#ifndef _LIBCPP_CXX03_LANG'?"); + + else if (condition.starts_with("_LIBCPP_STD_VER >= ") && + std::ranges::none_of(std::array{"14", "17", "20", "23"}, [&](auto val) { + return condition.find(val) != std::string_view::npos; + })) + check_.diag(location, "Not a valid value for _LIBCPP_STD_VER. Use 14, 17, 20 or 23"); + } + + clang::Preprocessor& preprocessor_; + clang::tidy::ClangTidyCheck& check_; +}; +} // namespace + +proper_version_checks::proper_version_checks(llvm::StringRef name, clang::tidy::ClangTidyContext* context) + : clang::tidy::ClangTidyCheck(name, context) {} + +void proper_version_checks::registerPPCallbacks( + const clang::SourceManager& source_manager, + clang::Preprocessor* preprocessor, + clang::Preprocessor* module_expander) { + preprocessor->addPPCallbacks(std::make_unique(*preprocessor, *this)); +} + +} // namespace libcpp