[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
This commit is contained in:
Nikolas Klauser 2023-02-17 14:01:19 +01:00
parent 6f216f0539
commit 07efa28314
7 changed files with 102 additions and 7 deletions

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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 @@ public:
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");

View File

@ -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

View File

@ -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