1 //===----------------------------------------------------------------------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "proper_version_checks.hpp" 10 11 #include <clang/Lex/Lexer.h> 12 #include <clang/Lex/PPCallbacks.h> 13 #include <clang/Lex/Preprocessor.h> 14 15 namespace libcpp { 16 namespace { 17 class proper_version_checks_callbacks : public clang::PPCallbacks { 18 public: 19 proper_version_checks_callbacks(clang::Preprocessor& preprocessor, clang::tidy::ClangTidyCheck& check) 20 : preprocessor_(preprocessor), check_(check) {} 21 22 void If(clang::SourceLocation location, clang::SourceRange condition_range, ConditionValueKind) override { 23 check_condition(location, condition_range); 24 } 25 26 void Elif(clang::SourceLocation location, 27 clang::SourceRange condition_range, 28 ConditionValueKind, 29 clang::SourceLocation if_location) override { 30 check_condition(location, condition_range); 31 } 32 33 private: 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)) 40 return; 41 42 if (condition == "__cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)") 43 return; 44 45 if (condition.starts_with("_LIBCPP_STD_VER") && condition.find(">") != std::string_view::npos && 46 condition.find(">=") == std::string_view::npos) 47 check_.diag(location, "_LIBCPP_STD_VER >= version should be used instead of _LIBCPP_STD_VER > prev_version"); 48 49 else if (condition.starts_with("__cplusplus")) 50 check_.diag(location, "Use _LIBCPP_STD_VER instead of __cplusplus to constrain based on the C++ version"); 51 52 else if (condition == "_LIBCPP_STD_VER >= 11") 53 check_.diag(location, "_LIBCPP_STD_VER >= 11 is always true. Did you mean '#ifndef _LIBCPP_CXX03_LANG'?"); 54 55 else if (condition.starts_with("_LIBCPP_STD_VER >= ") && 56 std::ranges::none_of(std::array{"14", "17", "20", "23", "26"}, [&](auto val) { 57 return condition.find(val) != std::string_view::npos; 58 })) 59 check_.diag(location, "Not a valid value for _LIBCPP_STD_VER. Use 14, 17, 20, 23, or 26"); 60 } 61 62 clang::Preprocessor& preprocessor_; 63 clang::tidy::ClangTidyCheck& check_; 64 }; 65 } // namespace 66 67 proper_version_checks::proper_version_checks(llvm::StringRef name, clang::tidy::ClangTidyContext* context) 68 : clang::tidy::ClangTidyCheck(name, context) {} 69 70 void proper_version_checks::registerPPCallbacks( 71 const clang::SourceManager& source_manager, 72 clang::Preprocessor* preprocessor, 73 clang::Preprocessor* module_expander) { 74 preprocessor->addPPCallbacks(std::make_unique<proper_version_checks_callbacks>(*preprocessor, *this)); 75 } 76 77 } // namespace libcpp 78