1 //===----------------------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "proper_version_checks.hpp"
11 #include <clang/Lex/Lexer.h>
12 #include <clang/Lex/PPCallbacks.h>
13 #include <clang/Lex/Preprocessor.h>
17 class proper_version_checks_callbacks
: public clang::PPCallbacks
{
19 proper_version_checks_callbacks(clang::Preprocessor
& preprocessor
, clang::tidy::ClangTidyCheck
& check
)
20 : preprocessor_(preprocessor
), check_(check
) {}
22 void If(clang::SourceLocation location
, clang::SourceRange condition_range
, ConditionValueKind
) override
{
23 check_condition(location
, condition_range
);
26 void Elif(clang::SourceLocation location
,
27 clang::SourceRange condition_range
,
29 clang::SourceLocation if_location
) override
{
30 check_condition(location
, condition_range
);
34 void check_condition(clang::SourceLocation location
, clang::SourceRange condition_range
) {
35 std::string_view condition
= clang::Lexer::getSourceText(
36 clang::CharSourceRange::getTokenRange(condition_range
),
37 preprocessor_
.getSourceManager(),
38 preprocessor_
.getLangOpts());
39 if (preprocessor_
.getSourceManager().isInMainFile(location
))
42 if (condition
.starts_with("_LIBCPP_STD_VER") && condition
.find(">") != std::string_view::npos
&&
43 condition
.find(">=") == std::string_view::npos
)
44 check_
.diag(location
, "_LIBCPP_STD_VER >= version should be used instead of _LIBCPP_STD_VER > prev_version");
46 else if (condition
.starts_with("__cplusplus"))
47 check_
.diag(location
, "Use _LIBCPP_STD_VER instead of __cplusplus to constrain based on the C++ version");
49 else if (condition
== "_LIBCPP_STD_VER >= 11")
50 check_
.diag(location
, "_LIBCPP_STD_VER >= 11 is always true. Did you mean '#ifndef _LIBCPP_CXX03_LANG'?");
52 else if (condition
.starts_with("_LIBCPP_STD_VER >= ") &&
53 std::ranges::none_of(std::array
{"14", "17", "20", "23", "26"}, [&](auto val
) {
54 return condition
.find(val
) != std::string_view::npos
;
56 check_
.diag(location
, "Not a valid value for _LIBCPP_STD_VER. Use 14, 17, 20, 23, or 26");
59 clang::Preprocessor
& preprocessor_
;
60 clang::tidy::ClangTidyCheck
& check_
;
64 proper_version_checks::proper_version_checks(llvm::StringRef name
, clang::tidy::ClangTidyContext
* context
)
65 : clang::tidy::ClangTidyCheck(name
, context
) {}
67 void proper_version_checks::registerPPCallbacks(
68 const clang::SourceManager
& source_manager
,
69 clang::Preprocessor
* preprocessor
,
70 clang::Preprocessor
* module_expander
) {
71 preprocessor
->addPPCallbacks(std::make_unique
<proper_version_checks_callbacks
>(*preprocessor
, *this));