blob: 173762e5602a710baa598fc9d9ac5c83886e6c4a [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
// This clang-tidy check ensures that we don't use any _LIBCPP_HAS_FOO macro
// inside `#ifdef`, `#ifndef` & friends, since the intent is to always use `#if` instead.
#include "internal_ftm_use.hpp"
#include <clang/Lex/Lexer.h>
#include <clang/Lex/PPCallbacks.h>
#include <clang/Lex/Preprocessor.h>
#include <string>
namespace libcpp {
namespace {
std::array valid_macros{
// Public API macros
"_LIBCPP_HAS_ASAN_CONTAINER_ANNOTATIONS_FOR_ALL_ALLOCATORS",
// Testing macros
"_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER",
// TODO: Why does this macro even exist?
"_LIBCPP_HAS_NO_TREE_BARRIER",
// Experimental features
"_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB",
"_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM",
"_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN",
"_LIBCPP_HAS_NO_INCOMPLETE_PSTL",
};
class internal_ftm_use_callbacks : public clang::PPCallbacks {
public:
internal_ftm_use_callbacks(clang::tidy::ClangTidyCheck& check) : check_(check) {}
void Defined(const clang::Token& token,
const clang::MacroDefinition& macro_definition,
clang::SourceRange location) override {
check_macro(token.getIdentifierInfo()->getName(), location.getBegin());
}
void Ifdef(clang::SourceLocation location, const clang::Token& token, const clang::MacroDefinition&) override {
check_macro(token.getIdentifierInfo()->getName(), location);
}
void Elifdef(clang::SourceLocation location, const clang::Token& token, const clang::MacroDefinition&) override {
check_macro(token.getIdentifierInfo()->getName(), location);
}
void Ifndef(clang::SourceLocation location, const clang::Token& token, const clang::MacroDefinition&) override {
check_macro(token.getIdentifierInfo()->getName(), location);
}
void Elifndef(clang::SourceLocation location, const clang::Token& token, const clang::MacroDefinition&) override {
check_macro(token.getIdentifierInfo()->getName(), location);
}
private:
void check_macro(std::string_view macro, clang::SourceLocation location) {
if (macro.starts_with("_LIBCPP_HAS_") && std::ranges::find(valid_macros, macro) == valid_macros.end()) {
check_.diag(location, std::string("\'") + std::string{macro} + "' is always defined to 1 or 0.");
}
}
clang::tidy::ClangTidyCheck& check_;
};
} // namespace
internal_ftm_use::internal_ftm_use(llvm::StringRef name, clang::tidy::ClangTidyContext* context)
: clang::tidy::ClangTidyCheck(name, context) {}
void internal_ftm_use::registerPPCallbacks(const clang::SourceManager& source_manager,
clang::Preprocessor* preprocessor,
clang::Preprocessor* module_expander) {
preprocessor->addPPCallbacks(std::make_unique<internal_ftm_use_callbacks>(*this));
}
} // namespace libcpp