[libcxx-commits] [libcxx] 07efa28 - [libc++] Add clang-tidy check for version checks

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Mar 8 06:17:31 PST 2023


Author: Nikolas Klauser
Date: 2023-03-08T15:17:25+01:00
New Revision: 07efa28314ce968dadadbcaff95cde2b9424a5ed

URL: https://github.com/llvm/llvm-project/commit/07efa28314ce968dadadbcaff95cde2b9424a5ed
DIFF: https://github.com/llvm/llvm-project/commit/07efa28314ce968dadadbcaff95cde2b9424a5ed.diff

LOG: [libc++] Add clang-tidy check for version checks

This check flags code which uses `_LIBCPP_STD_VER` the wrong way, or tries to use `__cplusplus`. It flags cases where we use `_LIBCPP_STD_VER >` instead of `_LIBCPP_STD_VER >=` and where wrong values are used (e.g. `_LIBCPP_STD_VER >= 24`).

Reviewed By: ldionne, #libc

Spies: libcxx-commits

Differential Revision: https://reviews.llvm.org/D144261

Added: 
    libcxx/test/tools/clang_tidy_checks/proper_version_checks.cpp
    libcxx/test/tools/clang_tidy_checks/proper_version_checks.hpp

Modified: 
    libcxx/include/__chrono/concepts.h
    libcxx/include/__config
    libcxx/include/iomanip
    libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
    libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__chrono/concepts.h b/libcxx/include/__chrono/concepts.h
index d7502ef9f84dc..38d6b0ccd538c 100644
--- 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 <class _Tp>
 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
index f531b1c057f84..95a75ea758d53 100644
--- 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
index bf8fbfe2a16f8..53445c72ba19a 100644
--- a/libcxx/include/iomanip
+++ b/libcxx/include/iomanip
@@ -513,8 +513,6 @@ put_time(const tm* __tm, const _CharT* __fmt)
     return __iom_t10<_CharT>(__tm, __fmt);
 }
 
-#if _LIBCPP_STD_VER >= 11
-
 template <class _CharT, class _Traits>
 _LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
 __quoted_output(basic_ostream<_CharT, _Traits>& __os,
@@ -622,8 +620,6 @@ __quoted(basic_string<_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT
     return __quoted_proxy<_CharT, _Traits, _Allocator>(__s, __delim, __escape);
 }
 
-#endif // _LIBCPP_STD_VER >= 11
-
 #if _LIBCPP_STD_VER >= 14
 
 template <class _CharT>

diff  --git a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
index 0e437d0a24308..bd1611fb0a9cc 100644
--- a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
+++ b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
@@ -17,6 +17,7 @@ endif()
 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
index 685d34a86b54a..9ecb41e20a2da 100644
--- 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 @@ class LibcxxTestModule : public clang::tidy::ClangTidyModule {
   void addCheckFactories(clang::tidy::ClangTidyCheckFactories& check_factories) override {
     check_factories.registerCheck<libcpp::abi_tag_on_virtual>("libcpp-avoid-abi-tag-on-virtual");
     check_factories.registerCheck<libcpp::hide_from_abi>("libcpp-hide-from-abi");
+    check_factories.registerCheck<libcpp::proper_version_checks>("libcpp-cpp-version-check");
     check_factories.registerCheck<libcpp::robust_against_adl_check>("libcpp-robust-against-adl");
     check_factories.registerCheck<libcpp::uglify_attributes>("libcpp-uglify-attributes");
     check_factories.registerCheck<libcpp::qualify_declval>("libcpp-qualify-declval");

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
index 0000000000000..2abc5ceeecfb8
--- /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 <clang/Lex/Lexer.h>
+#include <clang/Lex/PPCallbacks.h>
+#include <clang/Lex/Preprocessor.h>
+
+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<proper_version_checks_callbacks>(*preprocessor, *this));
+}
+
+} // namespace libcpp

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
index 0000000000000..5c33b9551bf27
--- /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


        


More information about the libcxx-commits mailing list