summaryrefslogtreecommitdiff
path: root/Utilities/ClangTidyModule
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/ClangTidyModule')
-rw-r--r--Utilities/ClangTidyModule/CMakeLists.txt37
-rw-r--r--Utilities/ClangTidyModule/Module.cxx38
-rw-r--r--Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx52
-rw-r--r--Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.h21
-rw-r--r--Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.cxx180
-rw-r--r--Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.h34
-rw-r--r--Utilities/ClangTidyModule/Tests/CMakeLists.txt18
-rw-r--r--Utilities/ClangTidyModule/Tests/RunClangTidy.cmake93
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-ostringstream-use-cmstrcat-stdout.txt6
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-ostringstream-use-cmstrcat.cxx10
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-fixit.cxx40
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-stdout.txt124
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat.cxx40
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-bespoke-enum-class-stdout.txt18
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-bespoke-enum-class.cxx63
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-fixit.cxx42
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stdout.txt52
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen.cxx42
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream-fixit.cxx81
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream-stdout.txt155
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream.cxx81
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once-stdout.txt25
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once.cxx5
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-both-fixit.h8
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-both.h10
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-include-guards-fixit.h6
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h9
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-neither-fixit.h5
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-neither.h4
-rw-r--r--Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once.h6
-rw-r--r--Utilities/ClangTidyModule/UseBespokeEnumClassCheck.cxx35
-rw-r--r--Utilities/ClangTidyModule/UseBespokeEnumClassCheck.h21
-rw-r--r--Utilities/ClangTidyModule/UseCmstrlenCheck.cxx78
-rw-r--r--Utilities/ClangTidyModule/UseCmstrlenCheck.h21
-rw-r--r--Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx101
-rw-r--r--Utilities/ClangTidyModule/UseCmsysFstreamCheck.h24
-rw-r--r--Utilities/ClangTidyModule/UsePragmaOnceCheck.cxx325
-rw-r--r--Utilities/ClangTidyModule/UsePragmaOnceCheck.h60
38 files changed, 1970 insertions, 0 deletions
diff --git a/Utilities/ClangTidyModule/CMakeLists.txt b/Utilities/ClangTidyModule/CMakeLists.txt
new file mode 100644
index 0000000000..97c176f635
--- /dev/null
+++ b/Utilities/ClangTidyModule/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+cmake_minimum_required(VERSION 3.13)
+project(CMakeClangTidyModule C CXX)
+
+get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
+get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
+
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Clang REQUIRED)
+
+add_library(cmake-clang-tidy-module MODULE
+ Module.cxx
+
+ OstringstreamUseCmstrcatCheck.cxx
+ OstringstreamUseCmstrcatCheck.h
+ StringConcatenationUseCmstrcatCheck.cxx
+ StringConcatenationUseCmstrcatCheck.h
+ UseBespokeEnumClassCheck.cxx
+ UseBespokeEnumClassCheck.h
+ UseCmstrlenCheck.cxx
+ UseCmstrlenCheck.h
+ UseCmsysFstreamCheck.cxx
+ UseCmsysFstreamCheck.h
+ UsePragmaOnceCheck.cxx
+ UsePragmaOnceCheck.h
+ )
+target_include_directories(cmake-clang-tidy-module PRIVATE ${CLANG_INCLUDE_DIRS})
+target_link_libraries(cmake-clang-tidy-module PRIVATE clang-tidy)
+
+option(RUN_TESTS "Run the tests for the clang-tidy module" OFF)
+if(RUN_TESTS)
+ enable_testing()
+ add_subdirectory(Tests)
+endif()
diff --git a/Utilities/ClangTidyModule/Module.cxx b/Utilities/ClangTidyModule/Module.cxx
new file mode 100644
index 0000000000..4dd7dcd007
--- /dev/null
+++ b/Utilities/ClangTidyModule/Module.cxx
@@ -0,0 +1,38 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include <clang-tidy/ClangTidyModule.h>
+#include <clang-tidy/ClangTidyModuleRegistry.h>
+
+#include "OstringstreamUseCmstrcatCheck.h"
+#include "StringConcatenationUseCmstrcatCheck.h"
+#include "UseBespokeEnumClassCheck.h"
+#include "UseCmstrlenCheck.h"
+#include "UseCmsysFstreamCheck.h"
+#include "UsePragmaOnceCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class CMakeClangTidyModule : public ClangTidyModule
+{
+public:
+ void addCheckFactories(ClangTidyCheckFactories& CheckFactories) override
+ {
+ CheckFactories.registerCheck<UseCmstrlenCheck>("cmake-use-cmstrlen");
+ CheckFactories.registerCheck<UseCmsysFstreamCheck>(
+ "cmake-use-cmsys-fstream");
+ CheckFactories.registerCheck<UseBespokeEnumClassCheck>(
+ "cmake-use-bespoke-enum-class");
+ CheckFactories.registerCheck<OstringstreamUseCmstrcatCheck>(
+ "cmake-ostringstream-use-cmstrcat");
+ CheckFactories.registerCheck<UsePragmaOnceCheck>("cmake-use-pragma-once");
+ CheckFactories.registerCheck<StringConcatenationUseCmstrcatCheck>(
+ "cmake-string-concatenation-use-cmstrcat");
+ }
+};
+
+static ClangTidyModuleRegistry::Add<CMakeClangTidyModule> X(
+ "cmake-clang-tidy", "Adds lint checks for the CMake code base.");
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx b/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx
new file mode 100644
index 0000000000..920fdf3a90
--- /dev/null
+++ b/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx
@@ -0,0 +1,52 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "OstringstreamUseCmstrcatCheck.h"
+
+#include <clang/AST/Type.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+using namespace ast_matchers;
+
+OstringstreamUseCmstrcatCheck::OstringstreamUseCmstrcatCheck(
+ StringRef Name, ClangTidyContext* Context)
+ : ClangTidyCheck(Name, Context)
+{
+}
+
+void OstringstreamUseCmstrcatCheck::registerMatchers(MatchFinder* Finder)
+{
+ Finder->addMatcher(
+ typeLoc(unless(elaboratedTypeLoc()),
+ optionally(hasParent(elaboratedTypeLoc().bind("parentType"))),
+ loc(qualType(
+ hasDeclaration(namedDecl(hasName("::std::ostringstream"))))))
+ .bind("ostringstream"),
+ this);
+}
+
+void OstringstreamUseCmstrcatCheck::check(
+ const MatchFinder::MatchResult& Result)
+{
+ const TypeLoc* ParentTypeNode =
+ Result.Nodes.getNodeAs<TypeLoc>("parentType");
+ const TypeLoc* RootNode = Result.Nodes.getNodeAs<TypeLoc>("ostringstream");
+
+ if (ParentTypeNode != nullptr) {
+ if (ParentTypeNode->getBeginLoc().isValid()) {
+ this->diag(ParentTypeNode->getBeginLoc(),
+ "use strings and cmStrCat() instead of std::ostringstream");
+ }
+
+ } else if (RootNode != nullptr) {
+ if (RootNode->getBeginLoc().isValid()) {
+ this->diag(RootNode->getBeginLoc(),
+ "use strings and cmStrCat() instead of std::ostringstream");
+ }
+ }
+}
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.h b/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.h
new file mode 100644
index 0000000000..ecb5616cfc
--- /dev/null
+++ b/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.h
@@ -0,0 +1,21 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <clang-tidy/ClangTidyCheck.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class OstringstreamUseCmstrcatCheck : public ClangTidyCheck
+{
+public:
+ OstringstreamUseCmstrcatCheck(StringRef Name, ClangTidyContext* Context);
+ void registerMatchers(ast_matchers::MatchFinder* Finder) override;
+
+ void check(const ast_matchers::MatchFinder::MatchResult& Result) override;
+};
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.cxx b/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.cxx
new file mode 100644
index 0000000000..e282d23d9b
--- /dev/null
+++ b/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.cxx
@@ -0,0 +1,180 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "StringConcatenationUseCmstrcatCheck.h"
+
+#include <cassert>
+
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+#include <clang/Lex/Lexer.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+using namespace ast_matchers;
+
+StringConcatenationUseCmstrcatCheck::StringConcatenationUseCmstrcatCheck(
+ StringRef Name, ClangTidyContext* Context)
+ : ClangTidyCheck(Name, Context)
+{
+}
+
+void StringConcatenationUseCmstrcatCheck::registerMatchers(MatchFinder* Finder)
+{
+ auto IsString = expr(hasType(qualType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(classTemplateSpecializationDecl(
+ hasName("::std::basic_string"),
+ hasTemplateArgument(
+ 0, templateArgument(refersToType(asString("char")))))))))));
+
+ auto IsChar = expr(hasType(asString("char")));
+
+ auto IsCharPtr = expr(hasType(pointerType(pointee(asString("const char")))));
+
+ auto IsStringConcat =
+ cxxOperatorCallExpr(hasOperatorName("+"),
+ anyOf(allOf(hasLHS(IsString), hasRHS(IsString)),
+ allOf(hasLHS(IsString), hasRHS(IsChar)),
+ allOf(hasLHS(IsString), hasRHS(IsCharPtr)),
+ allOf(hasLHS(IsChar), hasRHS(IsString)),
+ allOf(hasLHS(IsCharPtr), hasRHS(IsString))));
+
+ auto IsStringAppend = cxxOperatorCallExpr(
+ hasOperatorName("+="), hasLHS(IsString),
+ anyOf(hasRHS(IsString), hasRHS(IsChar), hasRHS(IsCharPtr)));
+
+ auto IsStringConcatWithLHS =
+ cxxOperatorCallExpr(
+ IsStringConcat,
+ optionally(hasLHS(materializeTemporaryExpr(
+ has(cxxBindTemporaryExpr(has(IsStringConcat.bind("lhs"))))))))
+ .bind("concat");
+
+ auto IsStringAppendWithRHS =
+ cxxOperatorCallExpr(
+ IsStringAppend,
+ optionally(hasRHS(materializeTemporaryExpr(has(implicitCastExpr(
+ has(cxxBindTemporaryExpr(has(IsStringConcat.bind("rhs"))))))))))
+ .bind("append");
+
+ Finder->addMatcher(IsStringConcatWithLHS, this);
+ Finder->addMatcher(IsStringAppendWithRHS, this);
+}
+
+void StringConcatenationUseCmstrcatCheck::check(
+ const MatchFinder::MatchResult& Result)
+{
+ const CXXOperatorCallExpr* AppendNode =
+ Result.Nodes.getNodeAs<CXXOperatorCallExpr>("append");
+ const CXXOperatorCallExpr* ConcatNode =
+ Result.Nodes.getNodeAs<CXXOperatorCallExpr>("concat");
+
+ if (AppendNode != nullptr) {
+ if (AppendNode->getBeginLoc().isValid()) {
+ assert(InProgressExprChains.find(AppendNode) ==
+ InProgressExprChains.end());
+
+ ExprChain TmpExprChain =
+ std::make_pair(OperatorType::PlusEquals,
+ std::vector<const CXXOperatorCallExpr*>{ AppendNode });
+ const CXXOperatorCallExpr* RHSNode =
+ Result.Nodes.getNodeAs<CXXOperatorCallExpr>("rhs");
+
+ if (RHSNode != nullptr) {
+ if (RHSNode->getBeginLoc().isValid()) {
+ InProgressExprChains[RHSNode] = std::move(TmpExprChain);
+ }
+ } else {
+ issueCorrection(TmpExprChain, Result);
+ }
+ }
+ }
+
+ if (ConcatNode != nullptr) {
+ if (ConcatNode->getBeginLoc().isValid()) {
+ ExprChain TmpExprChain;
+
+ if (!(InProgressExprChains.find(ConcatNode) ==
+ InProgressExprChains.end())) {
+ TmpExprChain = std::move(InProgressExprChains[ConcatNode]);
+ InProgressExprChains.erase(ConcatNode);
+ if (TmpExprChain.first == OperatorType::PlusEquals) {
+ TmpExprChain.second.insert(TmpExprChain.second.begin() + 1,
+ ConcatNode);
+ } else {
+ TmpExprChain.second.insert(TmpExprChain.second.begin(), ConcatNode);
+ }
+ } else {
+ TmpExprChain = std::make_pair(
+ OperatorType::Plus,
+ std::vector<const CXXOperatorCallExpr*>{ ConcatNode });
+ }
+
+ const CXXOperatorCallExpr* LHSNode =
+ Result.Nodes.getNodeAs<CXXOperatorCallExpr>("lhs");
+
+ if (LHSNode != nullptr) {
+ if (LHSNode->getBeginLoc().isValid()) {
+ InProgressExprChains[LHSNode] = std::move(TmpExprChain);
+ }
+ } else {
+ issueCorrection(TmpExprChain, Result);
+ }
+ }
+ }
+}
+
+void StringConcatenationUseCmstrcatCheck::issueCorrection(
+ const ExprChain& Chain, const MatchFinder::MatchResult& Result)
+{
+ std::vector<FixItHint> FixIts;
+ const CXXOperatorCallExpr* ExprNode;
+ std::vector<const clang::CXXOperatorCallExpr*>::const_iterator It =
+ Chain.second.begin();
+
+ if (Chain.first == OperatorType::PlusEquals) {
+ ExprNode = *It;
+ StringRef LHS = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(ExprNode->getArg(0)->getSourceRange()),
+ Result.Context->getSourceManager(), Result.Context->getLangOpts());
+
+ FixIts.push_back(FixItHint::CreateReplacement(
+ ExprNode->getExprLoc(), "= cmStrCat(" + LHS.str() + ","));
+ It++;
+ } else {
+ ExprNode = *It;
+ FixIts.push_back(
+ FixItHint::CreateInsertion(ExprNode->getBeginLoc(), "cmStrCat("));
+ }
+
+ while (It != std::end(Chain.second)) {
+ ExprNode = *It;
+ FixIts.push_back(
+ FixItHint::CreateReplacement(ExprNode->getOperatorLoc(), ","));
+ It++;
+ }
+ It--;
+ ExprNode = *It;
+
+ StringRef LastToken = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ ExprNode->getArg(1)->getSourceRange().getEnd()),
+ Result.Context->getSourceManager(), Result.Context->getLangOpts());
+ FixIts.push_back(FixItHint::CreateInsertion(
+ ExprNode->getEndLoc().getLocWithOffset(LastToken.str().size()), ")"));
+
+ It = Chain.second.begin();
+ ExprNode = *It;
+
+ if (Chain.first == OperatorType::PlusEquals) {
+ this->diag(ExprNode->getOperatorLoc(),
+ "use cmStrCat() instead of string append")
+ << FixIts;
+ } else {
+ this->diag(ExprNode->getBeginLoc(),
+ "use cmStrCat() instead of string concatenation")
+ << FixIts;
+ }
+}
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.h b/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.h
new file mode 100644
index 0000000000..43ff539c7a
--- /dev/null
+++ b/Utilities/ClangTidyModule/StringConcatenationUseCmstrcatCheck.h
@@ -0,0 +1,34 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <clang-tidy/ClangTidyCheck.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class StringConcatenationUseCmstrcatCheck : public ClangTidyCheck
+{
+public:
+ StringConcatenationUseCmstrcatCheck(StringRef Name,
+ ClangTidyContext* Context);
+ void registerMatchers(ast_matchers::MatchFinder* Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult& Result) override;
+
+private:
+ enum class OperatorType
+ {
+ Plus,
+ PlusEquals
+ };
+ typedef std::pair<OperatorType, std::vector<const CXXOperatorCallExpr*>>
+ ExprChain;
+ std::map<const CXXOperatorCallExpr*, ExprChain> InProgressExprChains;
+
+ void issueCorrection(const ExprChain& ExprChain,
+ const ast_matchers::MatchFinder::MatchResult& Result);
+};
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/Tests/CMakeLists.txt b/Utilities/ClangTidyModule/Tests/CMakeLists.txt
new file mode 100644
index 0000000000..8220f39e1d
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/CMakeLists.txt
@@ -0,0 +1,18 @@
+configure_file("${CMake_SOURCE_DIR}/.clang-format" ".clang-format" COPYONLY)
+
+function(add_run_clang_tidy_test check_name)
+ add_test(NAME "RunClangTidy.${check_name}" COMMAND ${CMAKE_COMMAND}
+ "-DCLANG_TIDY_COMMAND=$<TARGET_FILE:clang-tidy>"
+ "-DCLANG_TIDY_MODULE=$<TARGET_FILE:cmake-clang-tidy-module>"
+ "-DCHECK_NAME=${check_name}"
+ "-DRunClangTidy_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/RunClangTidy.cmake"
+ )
+endfunction()
+
+add_run_clang_tidy_test(cmake-use-cmstrlen)
+add_run_clang_tidy_test(cmake-use-cmsys-fstream)
+add_run_clang_tidy_test(cmake-use-bespoke-enum-class)
+add_run_clang_tidy_test(cmake-ostringstream-use-cmstrcat)
+add_run_clang_tidy_test(cmake-use-pragma-once)
+add_run_clang_tidy_test(cmake-string-concatenation-use-cmstrcat)
diff --git a/Utilities/ClangTidyModule/Tests/RunClangTidy.cmake b/Utilities/ClangTidyModule/Tests/RunClangTidy.cmake
new file mode 100644
index 0000000000..98770d7034
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/RunClangTidy.cmake
@@ -0,0 +1,93 @@
+set(config_arg)
+if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.clang-tidy")
+ set(config_arg "--config-file=${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.clang-tidy")
+endif()
+
+if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-stdout.txt")
+ file(READ "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-stdout.txt" expect_stdout)
+ string(REGEX REPLACE "\n+$" "" expect_stdout "${expect_stdout}")
+else()
+ set(expect_stdout "")
+endif()
+
+set(source_file "${RunClangTidy_BINARY_DIR}/${CHECK_NAME}.cxx")
+configure_file("${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.cxx" "${source_file}" COPYONLY)
+
+file(GLOB header_files RELATIVE "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}" "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}/*")
+file(REMOVE_RECURSE "${RunClangTiy_BINARY_DIR}/${CHECK_NAME}")
+foreach(header_file IN LISTS header_files)
+ if(NOT header_file MATCHES "-fixit\\.h\$")
+ file(MAKE_DIRECTORY "${RunClangTidy_BINARY_DIR}/${CHECK_NAME}")
+ configure_file("${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}/${header_file}" "${RunClangTidy_BINARY_DIR}/${CHECK_NAME}/${header_file}" COPYONLY)
+ endif()
+endforeach()
+
+set(command
+ "${CLANG_TIDY_COMMAND}"
+ "--load=${CLANG_TIDY_MODULE}"
+ "--checks=-*,${CHECK_NAME}"
+ "--fix"
+ "--format-style=file"
+ "--header-filter=/${CHECK_NAME}/"
+ ${config_arg}
+ "${source_file}"
+ --
+ )
+execute_process(
+ COMMAND ${command}
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE actual_stdout
+ ERROR_VARIABLE actual_stderr
+ )
+string(REPLACE "${RunClangTidy_BINARY_DIR}/" "" actual_stdout "${actual_stdout}")
+
+set(RunClangTidy_TEST_FAILED)
+
+if(NOT result EQUAL 0)
+ string(APPEND RunClangTidy_TEST_FAILED "Expected result: 0, actual result: ${result}\n")
+endif()
+
+string(REGEX REPLACE "\n+$" "" actual_stdout "${actual_stdout}")
+if(NOT actual_stdout STREQUAL expect_stdout)
+ string(REPLACE "\n" "\n " expect_stdout_formatted " ${expect_stdout}")
+ string(REPLACE "\n" "\n " actual_stdout_formatted " ${actual_stdout}")
+ string(APPEND RunClangTidy_TEST_FAILED "Expected stdout:\n${expect_stdout_formatted}\nActual stdout:\n${actual_stdout_formatted}\n")
+endif()
+
+function(check_fixit expected fallback_expected actual)
+ if(EXISTS "${expected}")
+ set(expect_fixit_file "${expected}")
+ else()
+ set(expect_fixit_file "${fallback_expected}")
+ endif()
+ file(READ "${expect_fixit_file}" expect_fixit)
+ file(READ "${actual}" actual_fixit)
+ if(NOT expect_fixit STREQUAL actual_fixit)
+ string(REPLACE "\n" "\n " expect_fixit_formatted " ${expect_fixit}")
+ string(REPLACE "\n" "\n " actual_fixit_formatted " ${actual_fixit}")
+ string(APPEND RunClangTidy_TEST_FAILED "Expected fixit for ${actual}:\n${expect_fixit_formatted}\nActual fixit:\n${actual_fixit_formatted}\n")
+ set(RunClangTidy_TEST_FAILED "${RunClangTidy_TEST_FAILED}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+check_fixit(
+ "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}-fixit.cxx"
+ "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}.cxx"
+ "${source_file}"
+ )
+
+foreach(header_file IN LISTS header_files)
+ if(NOT header_file MATCHES "-fixit\\.h\$")
+ string(REGEX REPLACE "\\.h\$" "-fixit.h" header_fixit "${header_file}")
+ check_fixit(
+ "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}/${header_fixit}"
+ "${CMAKE_CURRENT_LIST_DIR}/${CHECK_NAME}/${header_file}"
+ "${RunClangTidy_BINARY_DIR}/${CHECK_NAME}/${header_file}"
+ )
+ endif()
+endforeach()
+
+if(RunClangTidy_TEST_FAILED)
+ string(REPLACE ";" " " command_formatted "${command}")
+ message(FATAL_ERROR "Command:\n ${command_formatted}\n${RunClangTidy_TEST_FAILED}")
+endif()
diff --git a/Utilities/ClangTidyModule/Tests/cmake-ostringstream-use-cmstrcat-stdout.txt b/Utilities/ClangTidyModule/Tests/cmake-ostringstream-use-cmstrcat-stdout.txt
new file mode 100644
index 0000000000..1b2d6e740e
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-ostringstream-use-cmstrcat-stdout.txt
@@ -0,0 +1,6 @@
+cmake-ostringstream-use-cmstrcat.cxx:5:3: warning: use strings and cmStrCat() instead of std::ostringstream [cmake-ostringstream-use-cmstrcat]
+ std::ostringstream test;
+ ^
+cmake-ostringstream-use-cmstrcat.cxx:8:13: warning: use strings and cmStrCat() instead of std::ostringstream [cmake-ostringstream-use-cmstrcat]
+void check2(std::ostringstream& test2)
+ ^
diff --git a/Utilities/ClangTidyModule/Tests/cmake-ostringstream-use-cmstrcat.cxx b/Utilities/ClangTidyModule/Tests/cmake-ostringstream-use-cmstrcat.cxx
new file mode 100644
index 0000000000..ab749a6928
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-ostringstream-use-cmstrcat.cxx
@@ -0,0 +1,10 @@
+#include <sstream>
+
+void check()
+{
+ std::ostringstream test;
+}
+
+void check2(std::ostringstream& test2)
+{
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-fixit.cxx b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-fixit.cxx
new file mode 100644
index 0000000000..dd1e6c4e89
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-fixit.cxx
@@ -0,0 +1,40 @@
+#include <string>
+#include <utility>
+
+template <typename... Args>
+std::string cmStrCat(Args&&... args)
+{
+ return "";
+}
+
+std::string a = "This is a string variable";
+std::string b = " and this is a string variable";
+std::string concat;
+
+// Correction needed
+void test1()
+{
+ concat = cmStrCat(a, b);
+ concat = cmStrCat(a, " and this is a string literal");
+ concat = cmStrCat(a, 'O');
+ concat = cmStrCat("This is a string literal", b);
+ concat = cmStrCat('O', a);
+ concat = cmStrCat(a, " and this is a string literal", 'O', b);
+
+ concat = cmStrCat(concat, b);
+ concat = cmStrCat(concat, " and this is a string literal");
+ concat = cmStrCat(concat, 'o');
+ concat = cmStrCat(concat, b, " and this is a string literal ", 'o', b);
+
+ std::pair<std::string, std::string> p;
+ concat = cmStrCat(p.first, p.second);
+}
+
+// No correction needed
+void test2()
+{
+ a = b;
+ a = "This is a string literal";
+ a = 'X';
+ cmStrCat(a, b);
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-stdout.txt b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-stdout.txt
new file mode 100644
index 0000000000..83b8d83d8f
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat-stdout.txt
@@ -0,0 +1,124 @@
+cmake-string-concatenation-use-cmstrcat.cxx:17:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+ concat = a + b;
+ ^ ~
+ cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:17:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:17:14: note: FIX-IT applied suggested code changes
+ concat = a + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:17:17: note: FIX-IT applied suggested code changes
+ concat = a + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:18:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+ concat = a + " and this is a string literal";
+ ^ ~
+ cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:18:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:18:14: note: FIX-IT applied suggested code changes
+ concat = a + " and this is a string literal";
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:18:47: note: FIX-IT applied suggested code changes
+ concat = a + " and this is a string literal";
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:19:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+ concat = a + 'O';
+ ^ ~
+ cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:19:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:19:14: note: FIX-IT applied suggested code changes
+ concat = a + 'O';
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:19:19: note: FIX-IT applied suggested code changes
+ concat = a + 'O';
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:20:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+ concat = "This is a string literal" + b;
+ ^ ~
+ cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:20:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:20:39: note: FIX-IT applied suggested code changes
+ concat = "This is a string literal" + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:20:42: note: FIX-IT applied suggested code changes
+ concat = "This is a string literal" + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:21:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+ concat = 'O' + a;
+ ^ ~
+ cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:21:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:21:16: note: FIX-IT applied suggested code changes
+ concat = 'O' + a;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:21:19: note: FIX-IT applied suggested code changes
+ concat = 'O' + a;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:22:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+ concat = a + " and this is a string literal" + 'O' + b;
+ ^ ~ ~ ~
+ cmStrCat( , , , )
+cmake-string-concatenation-use-cmstrcat.cxx:22:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:22:14: note: FIX-IT applied suggested code changes
+ concat = a + " and this is a string literal" + 'O' + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:22:48: note: FIX-IT applied suggested code changes
+ concat = a + " and this is a string literal" + 'O' + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:22:54: note: FIX-IT applied suggested code changes
+ concat = a + " and this is a string literal" + 'O' + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:22:57: note: FIX-IT applied suggested code changes
+ concat = a + " and this is a string literal" + 'O' + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:24:10: warning: use cmStrCat() instead of string append [cmake-string-concatenation-use-cmstrcat]
+ concat += b;
+ ^~
+ = cmStrCat(concat, )
+cmake-string-concatenation-use-cmstrcat.cxx:24:10: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:24:14: note: FIX-IT applied suggested code changes
+ concat += b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:25:10: warning: use cmStrCat() instead of string append [cmake-string-concatenation-use-cmstrcat]
+ concat += " and this is a string literal";
+ ^~
+ = cmStrCat(concat, )
+cmake-string-concatenation-use-cmstrcat.cxx:25:10: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:25:44: note: FIX-IT applied suggested code changes
+ concat += " and this is a string literal";
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:26:10: warning: use cmStrCat() instead of string append [cmake-string-concatenation-use-cmstrcat]
+ concat += 'o';
+ ^~
+ = cmStrCat(concat, )
+cmake-string-concatenation-use-cmstrcat.cxx:26:10: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:26:16: note: FIX-IT applied suggested code changes
+ concat += 'o';
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:27:10: warning: use cmStrCat() instead of string append [cmake-string-concatenation-use-cmstrcat]
+ concat += b + " and this is a string literal " + 'o' + b;
+ ^~ ~ ~ ~
+ = cmStrCat(concat, , , , )
+cmake-string-concatenation-use-cmstrcat.cxx:27:10: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:27:15: note: FIX-IT applied suggested code changes
+ concat += b + " and this is a string literal " + 'o' + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:27:50: note: FIX-IT applied suggested code changes
+ concat += b + " and this is a string literal " + 'o' + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:27:56: note: FIX-IT applied suggested code changes
+ concat += b + " and this is a string literal " + 'o' + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:27:59: note: FIX-IT applied suggested code changes
+ concat += b + " and this is a string literal " + 'o' + b;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:30:12: warning: use cmStrCat() instead of string concatenation [cmake-string-concatenation-use-cmstrcat]
+ concat = p.first + p.second;
+ ^ ~
+ cmStrCat( , )
+cmake-string-concatenation-use-cmstrcat.cxx:30:12: note: FIX-IT applied suggested code changes
+cmake-string-concatenation-use-cmstrcat.cxx:30:20: note: FIX-IT applied suggested code changes
+ concat = p.first + p.second;
+ ^
+cmake-string-concatenation-use-cmstrcat.cxx:30:30: note: FIX-IT applied suggested code changes
+ concat = p.first + p.second;
+ ^
diff --git a/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat.cxx b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat.cxx
new file mode 100644
index 0000000000..b088ca3f52
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-string-concatenation-use-cmstrcat.cxx
@@ -0,0 +1,40 @@
+#include <string>
+#include <utility>
+
+template <typename... Args>
+std::string cmStrCat(Args&&... args)
+{
+ return "";
+}
+
+std::string a = "This is a string variable";
+std::string b = " and this is a string variable";
+std::string concat;
+
+// Correction needed
+void test1()
+{
+ concat = a + b;
+ concat = a + " and this is a string literal";
+ concat = a + 'O';
+ concat = "This is a string literal" + b;
+ concat = 'O' + a;
+ concat = a + " and this is a string literal" + 'O' + b;
+
+ concat += b;
+ concat += " and this is a string literal";
+ concat += 'o';
+ concat += b + " and this is a string literal " + 'o' + b;
+
+ std::pair<std::string, std::string> p;
+ concat = p.first + p.second;
+}
+
+// No correction needed
+void test2()
+{
+ a = b;
+ a = "This is a string literal";
+ a = 'X';
+ cmStrCat(a, b);
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-bespoke-enum-class-stdout.txt b/Utilities/ClangTidyModule/Tests/cmake-use-bespoke-enum-class-stdout.txt
new file mode 100644
index 0000000000..5e0acddc66
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-bespoke-enum-class-stdout.txt
@@ -0,0 +1,18 @@
+cmake-use-bespoke-enum-class.cxx:3:16: warning: use a bespoke enum class instead of booleans as parameters [cmake-use-bespoke-enum-class]
+bool function1(bool i)
+ ^
+cmake-use-bespoke-enum-class.cxx:8:15: warning: use a bespoke enum class instead of booleans as parameters [cmake-use-bespoke-enum-class]
+int function2(bool i)
+ ^
+cmake-use-bespoke-enum-class.cxx:13:16: warning: use a bespoke enum class instead of booleans as parameters [cmake-use-bespoke-enum-class]
+char function3(bool i)
+ ^
+cmake-use-bespoke-enum-class.cxx:18:16: warning: use a bespoke enum class instead of booleans as parameters [cmake-use-bespoke-enum-class]
+void function4(bool i)
+ ^
+cmake-use-bespoke-enum-class.cxx:22:17: warning: use a bespoke enum class instead of booleans as parameters [cmake-use-bespoke-enum-class]
+float function5(bool i)
+ ^
+cmake-use-bespoke-enum-class.cxx:27:18: warning: use a bespoke enum class instead of booleans as parameters [cmake-use-bespoke-enum-class]
+double function6(bool i)
+ ^
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-bespoke-enum-class.cxx b/Utilities/ClangTidyModule/Tests/cmake-use-bespoke-enum-class.cxx
new file mode 100644
index 0000000000..2913e6a2d6
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-bespoke-enum-class.cxx
@@ -0,0 +1,63 @@
+// Correction needed
+
+bool function1(bool i)
+{
+ return true;
+}
+
+int function2(bool i)
+{
+ return 0;
+}
+
+char function3(bool i)
+{
+ return 'a';
+}
+
+void function4(bool i)
+{
+}
+
+float function5(bool i)
+{
+ return 1.0;
+}
+
+double function6(bool i)
+{
+ return 0;
+}
+
+// No correction needed
+bool global;
+
+bool function7(int i)
+{
+ bool l;
+ return true;
+}
+
+int function8(int i)
+{
+ return i;
+}
+
+char function9(char i)
+{
+ return i;
+}
+
+void function10()
+{
+}
+
+float function11(float i)
+{
+ return i;
+}
+
+double function12(double i)
+{
+ return i;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-fixit.cxx b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-fixit.cxx
new file mode 100644
index 0000000000..cde00864f1
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-fixit.cxx
@@ -0,0 +1,42 @@
+#include <cstring>
+
+template <size_t N>
+constexpr size_t cmStrLen(const char (&/*str*/)[N])
+{
+ return N - 1;
+}
+
+namespace ns1 {
+using std::strlen;
+}
+
+namespace ns2 {
+std::size_t strlen(const char* str)
+{
+ return std::strlen(str);
+}
+}
+
+int main()
+{
+ // String variable used for calling strlen() on a variable
+ auto s0 = "howdy";
+
+ // Correction needed
+ (void)cmStrLen("Hello");
+ (void)cmStrLen("Goodbye");
+ (void)cmStrLen("Hola");
+ (void)cmStrLen("Bonjour");
+ (void)(cmStrLen("Hallo"));
+ (void)(4 + cmStrLen("Hallo"));
+ (void)(cmStrLen("Hallo"));
+ (void)(4 + cmStrLen("Hallo"));
+
+ // No correction needed
+ (void)ns2::strlen("Salve");
+ (void)cmStrLen("Konnichiwa");
+ (void)strlen(s0);
+ (void)(sizeof("Hallo") - 2);
+
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stdout.txt b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stdout.txt
new file mode 100644
index 0000000000..d18822a4f4
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen-stdout.txt
@@ -0,0 +1,52 @@
+cmake-use-cmstrlen.cxx:26:9: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+ (void)strlen("Hello");
+ ^~~~~~
+ cmStrLen
+cmake-use-cmstrlen.cxx:26:9: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:27:9: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+ (void)::strlen("Goodbye");
+ ^~~~~~~~
+ cmStrLen
+cmake-use-cmstrlen.cxx:27:9: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:28:9: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+ (void)std::strlen("Hola");
+ ^~~~~~~~~~~
+ cmStrLen
+cmake-use-cmstrlen.cxx:28:9: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:29:9: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+ (void)ns1::strlen("Bonjour");
+ ^~~~~~~~~~~
+ cmStrLen
+cmake-use-cmstrlen.cxx:29:9: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:30:10: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+ (void)(sizeof("Hallo") - 1);
+ ^~~~~~ ~~~
+ cmStrLen
+cmake-use-cmstrlen.cxx:30:10: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:30:26: note: FIX-IT applied suggested code changes
+ (void)(sizeof("Hallo") - 1);
+ ^
+cmake-use-cmstrlen.cxx:31:14: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+ (void)(4 + sizeof("Hallo") - 1);
+ ^~~~~~ ~~~
+ cmStrLen
+cmake-use-cmstrlen.cxx:31:14: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:31:30: note: FIX-IT applied suggested code changes
+ (void)(4 + sizeof("Hallo") - 1);
+ ^
+cmake-use-cmstrlen.cxx:32:10: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+ (void)(sizeof "Hallo" - 1);
+ ^~~~~~ ~~~
+ cmStrLen( )
+cmake-use-cmstrlen.cxx:32:10: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:32:25: note: FIX-IT applied suggested code changes
+ (void)(sizeof "Hallo" - 1);
+ ^
+cmake-use-cmstrlen.cxx:33:14: warning: use cmStrLen() for string literals [cmake-use-cmstrlen]
+ (void)(4 + sizeof "Hallo" - 1);
+ ^~~~~~ ~~~
+ cmStrLen( )
+cmake-use-cmstrlen.cxx:33:14: note: FIX-IT applied suggested code changes
+cmake-use-cmstrlen.cxx:33:29: note: FIX-IT applied suggested code changes
+ (void)(4 + sizeof "Hallo" - 1);
+ ^
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen.cxx b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen.cxx
new file mode 100644
index 0000000000..205bc9cc85
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmstrlen.cxx
@@ -0,0 +1,42 @@
+#include <cstring>
+
+template <size_t N>
+constexpr size_t cmStrLen(const char (&/*str*/)[N])
+{
+ return N - 1;
+}
+
+namespace ns1 {
+using std::strlen;
+}
+
+namespace ns2 {
+std::size_t strlen(const char* str)
+{
+ return std::strlen(str);
+}
+}
+
+int main()
+{
+ // String variable used for calling strlen() on a variable
+ auto s0 = "howdy";
+
+ // Correction needed
+ (void)strlen("Hello");
+ (void)::strlen("Goodbye");
+ (void)std::strlen("Hola");
+ (void)ns1::strlen("Bonjour");
+ (void)(sizeof("Hallo") - 1);
+ (void)(4 + sizeof("Hallo") - 1);
+ (void)(sizeof "Hallo" - 1);
+ (void)(4 + sizeof "Hallo" - 1);
+
+ // No correction needed
+ (void)ns2::strlen("Salve");
+ (void)cmStrLen("Konnichiwa");
+ (void)strlen(s0);
+ (void)(sizeof("Hallo") - 2);
+
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream-fixit.cxx b/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream-fixit.cxx
new file mode 100644
index 0000000000..5c7c12323f
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream-fixit.cxx
@@ -0,0 +1,81 @@
+#include <fstream>
+#include <vector>
+
+namespace cmsys {
+using std::ifstream;
+using std::ofstream;
+using std::fstream;
+}
+
+namespace ns {
+using std::ifstream;
+using std::ofstream;
+using std::fstream;
+
+namespace ns {
+using std::ifstream;
+using std::ofstream;
+using std::fstream;
+}
+
+class cl
+{
+public:
+ using ifstream = cmsys::ifstream;
+ using ofstream = cmsys::ofstream;
+ using fstream = cmsys::fstream;
+};
+
+using ifs = cmsys::ifstream;
+using ofs = cmsys::ofstream;
+using fs = cmsys::fstream;
+}
+
+int main()
+{
+ using std::ifstream;
+ using std::ofstream;
+ using std::fstream;
+
+ // Correction needed
+ cmsys::ifstream ifsUnqual;
+ cmsys::ifstream ifsQual;
+ cmsys::ifstream ifsNS;
+ cmsys::ifstream ifsNested;
+ cmsys::ifstream ifsClass;
+ cmsys::ifstream ifsRenamed;
+
+ cmsys::ofstream ofsUnqual;
+ cmsys::ofstream ofsQual;
+ cmsys::ofstream ofsNS;
+ cmsys::ofstream ofsNested;
+ cmsys::ofstream ofsClass;
+ cmsys::ofstream ofsRenamed;
+
+ cmsys::fstream fsUnqual;
+ cmsys::fstream fsQual;
+ cmsys::fstream fsNS;
+ cmsys::fstream fsNested;
+ cmsys::fstream fsClass;
+ cmsys::fstream fsRenamed;
+
+ cmsys::ifstream::off_type offsetQual = 0;
+ cmsys::ifstream::off_type offsetUnqual = 0;
+ cmsys::ifstream::off_type offsetNS = 0;
+ cmsys::ifstream::off_type offsetNested = 0;
+ cmsys::ifstream::traits_type::off_type offsetTraitsNested = 0;
+ cmsys::ifstream::traits_type::off_type offsetTraitsClass = 0;
+
+ std::vector<cmsys::ifstream> ifsVectorUnqual;
+
+ // No correction needed
+ cmsys::ifstream ifsCmsys;
+ cmsys::ofstream ofsCmsys;
+ cmsys::fstream fsCmsys;
+ cmsys::ifstream::off_type offsetCmsys = 0;
+ cmsys::ifstream::traits_type::off_type offsetTraitsCmsys = 0;
+ std::vector<cmsys::ifstream> ifsVectorCmsys;
+ std::basic_ifstream<wchar_t> ifsWchar;
+
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream-stdout.txt b/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream-stdout.txt
new file mode 100644
index 0000000000..d2c45f2c10
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream-stdout.txt
@@ -0,0 +1,155 @@
+cmake-use-cmsys-fstream.cxx:24:20: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ using ifstream = std::ifstream;
+ ^~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:24:20: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:25:20: warning: use cmsys::ofstream [cmake-use-cmsys-fstream]
+ using ofstream = std::ofstream;
+ ^~~~~~~~~~~~~
+ cmsys::ofstream
+cmake-use-cmsys-fstream.cxx:25:20: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:26:19: warning: use cmsys::fstream [cmake-use-cmsys-fstream]
+ using fstream = std::fstream;
+ ^~~~~~~~~~~~
+ cmsys::fstream
+cmake-use-cmsys-fstream.cxx:26:19: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:29:13: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+using ifs = std::ifstream;
+ ^~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:29:13: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:30:13: warning: use cmsys::ofstream [cmake-use-cmsys-fstream]
+using ofs = std::ofstream;
+ ^~~~~~~~~~~~~
+ cmsys::ofstream
+cmake-use-cmsys-fstream.cxx:30:13: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:31:12: warning: use cmsys::fstream [cmake-use-cmsys-fstream]
+using fs = std::fstream;
+ ^~~~~~~~~~~~
+ cmsys::fstream
+cmake-use-cmsys-fstream.cxx:31:12: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:41:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ifstream ifsUnqual;
+ ^~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:41:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:42:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ std::ifstream ifsQual;
+ ^~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:42:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:43:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ns::ifstream ifsNS;
+ ^~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:43:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:44:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ns::ns::ifstream ifsNested;
+ ^~~~~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:44:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:45:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ns::cl::ifstream ifsClass;
+ ^~~~~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:45:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:46:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ns::ifs ifsRenamed;
+ ^~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:46:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:48:3: warning: use cmsys::ofstream [cmake-use-cmsys-fstream]
+ ofstream ofsUnqual;
+ ^~~~~~~~
+ cmsys::ofstream
+cmake-use-cmsys-fstream.cxx:48:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:49:3: warning: use cmsys::ofstream [cmake-use-cmsys-fstream]
+ std::ofstream ofsQual;
+ ^~~~~~~~~~~~~
+ cmsys::ofstream
+cmake-use-cmsys-fstream.cxx:49:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:50:3: warning: use cmsys::ofstream [cmake-use-cmsys-fstream]
+ ns::ofstream ofsNS;
+ ^~~~~~~~~~~~
+ cmsys::ofstream
+cmake-use-cmsys-fstream.cxx:50:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:51:3: warning: use cmsys::ofstream [cmake-use-cmsys-fstream]
+ ns::ns::ofstream ofsNested;
+ ^~~~~~~~~~~~~~~~
+ cmsys::ofstream
+cmake-use-cmsys-fstream.cxx:51:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:52:3: warning: use cmsys::ofstream [cmake-use-cmsys-fstream]
+ ns::cl::ofstream ofsClass;
+ ^~~~~~~~~~~~~~~~
+ cmsys::ofstream
+cmake-use-cmsys-fstream.cxx:52:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:53:3: warning: use cmsys::ofstream [cmake-use-cmsys-fstream]
+ ns::ofs ofsRenamed;
+ ^~~~~~~
+ cmsys::ofstream
+cmake-use-cmsys-fstream.cxx:53:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:55:3: warning: use cmsys::fstream [cmake-use-cmsys-fstream]
+ fstream fsUnqual;
+ ^~~~~~~
+ cmsys::fstream
+cmake-use-cmsys-fstream.cxx:55:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:56:3: warning: use cmsys::fstream [cmake-use-cmsys-fstream]
+ std::fstream fsQual;
+ ^~~~~~~~~~~~
+ cmsys::fstream
+cmake-use-cmsys-fstream.cxx:56:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:57:3: warning: use cmsys::fstream [cmake-use-cmsys-fstream]
+ ns::fstream fsNS;
+ ^~~~~~~~~~~
+ cmsys::fstream
+cmake-use-cmsys-fstream.cxx:57:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:58:3: warning: use cmsys::fstream [cmake-use-cmsys-fstream]
+ ns::ns::fstream fsNested;
+ ^~~~~~~~~~~~~~~
+ cmsys::fstream
+cmake-use-cmsys-fstream.cxx:58:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:59:3: warning: use cmsys::fstream [cmake-use-cmsys-fstream]
+ ns::ns::fstream fsClass;
+ ^~~~~~~~~~~~~~~
+ cmsys::fstream
+cmake-use-cmsys-fstream.cxx:59:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:60:3: warning: use cmsys::fstream [cmake-use-cmsys-fstream]
+ ns::fs fsRenamed;
+ ^~~~~~
+ cmsys::fstream
+cmake-use-cmsys-fstream.cxx:60:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:62:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ std::ifstream::off_type offsetQual = 0;
+ ^~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:62:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:63:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ifstream::off_type offsetUnqual = 0;
+ ^~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:63:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:64:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ns::ifstream::off_type offsetNS = 0;
+ ^~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:64:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:65:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ns::ns::ifstream::off_type offsetNested = 0;
+ ^~~~~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:65:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:66:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ns::ns::ifstream::traits_type::off_type offsetTraitsNested = 0;
+ ^~~~~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:66:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:67:3: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ ns::cl::ifstream::traits_type::off_type offsetTraitsClass = 0;
+ ^~~~~~~~~~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:67:3: note: FIX-IT applied suggested code changes
+cmake-use-cmsys-fstream.cxx:69:15: warning: use cmsys::ifstream [cmake-use-cmsys-fstream]
+ std::vector<ifstream> ifsVectorUnqual;
+ ^~~~~~~~
+ cmsys::ifstream
+cmake-use-cmsys-fstream.cxx:69:15: note: FIX-IT applied suggested code changes
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream.cxx b/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream.cxx
new file mode 100644
index 0000000000..56a7611dcb
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-cmsys-fstream.cxx
@@ -0,0 +1,81 @@
+#include <fstream>
+#include <vector>
+
+namespace cmsys {
+using std::ifstream;
+using std::ofstream;
+using std::fstream;
+}
+
+namespace ns {
+using std::ifstream;
+using std::ofstream;
+using std::fstream;
+
+namespace ns {
+using std::ifstream;
+using std::ofstream;
+using std::fstream;
+}
+
+class cl
+{
+public:
+ using ifstream = std::ifstream;
+ using ofstream = std::ofstream;
+ using fstream = std::fstream;
+};
+
+using ifs = std::ifstream;
+using ofs = std::ofstream;
+using fs = std::fstream;
+}
+
+int main()
+{
+ using std::ifstream;
+ using std::ofstream;
+ using std::fstream;
+
+ // Correction needed
+ ifstream ifsUnqual;
+ std::ifstream ifsQual;
+ ns::ifstream ifsNS;
+ ns::ns::ifstream ifsNested;
+ ns::cl::ifstream ifsClass;
+ ns::ifs ifsRenamed;
+
+ ofstream ofsUnqual;
+ std::ofstream ofsQual;
+ ns::ofstream ofsNS;
+ ns::ns::ofstream ofsNested;
+ ns::cl::ofstream ofsClass;
+ ns::ofs ofsRenamed;
+
+ fstream fsUnqual;
+ std::fstream fsQual;
+ ns::fstream fsNS;
+ ns::ns::fstream fsNested;
+ ns::ns::fstream fsClass;
+ ns::fs fsRenamed;
+
+ std::ifstream::off_type offsetQual = 0;
+ ifstream::off_type offsetUnqual = 0;
+ ns::ifstream::off_type offsetNS = 0;
+ ns::ns::ifstream::off_type offsetNested = 0;
+ ns::ns::ifstream::traits_type::off_type offsetTraitsNested = 0;
+ ns::cl::ifstream::traits_type::off_type offsetTraitsClass = 0;
+
+ std::vector<ifstream> ifsVectorUnqual;
+
+ // No correction needed
+ cmsys::ifstream ifsCmsys;
+ cmsys::ofstream ofsCmsys;
+ cmsys::fstream fsCmsys;
+ cmsys::ifstream::off_type offsetCmsys = 0;
+ cmsys::ifstream::traits_type::off_type offsetTraitsCmsys = 0;
+ std::vector<cmsys::ifstream> ifsVectorCmsys;
+ std::basic_ifstream<wchar_t> ifsWchar;
+
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once-stdout.txt b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once-stdout.txt
new file mode 100644
index 0000000000..e80e4a4f80
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once-stdout.txt
@@ -0,0 +1,25 @@
+cmake-use-pragma-once/cmake-use-pragma-once-both.h:1:1: warning: use #pragma once [cmake-use-pragma-once]
+#ifndef BOTH_H
+^~~~~~~~~~~~~~
+cmake-use-pragma-once/cmake-use-pragma-once-both.h:1:1: note: FIX-IT applied suggested code changes
+cmake-use-pragma-once/cmake-use-pragma-once-both.h:2:1: note: FIX-IT applied suggested code changes
+#define BOTH_H
+^
+cmake-use-pragma-once/cmake-use-pragma-once-both.h:10:1: note: FIX-IT applied suggested code changes
+#endif
+^
+cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h:1:1: warning: use #pragma once [cmake-use-pragma-once]
+#ifndef INCLUDE_GUARDS_H
+^~~~~~~~~~~~~~~~~~~~~~~~
+#pragma once
+cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h:1:1: note: FIX-IT applied suggested code changes
+cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h:2:1: note: FIX-IT applied suggested code changes
+#define INCLUDE_GUARDS_H
+^
+cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h:9:1: note: FIX-IT applied suggested code changes
+#endif
+^
+cmake-use-pragma-once/cmake-use-pragma-once-neither.h:1:1: warning: use #pragma once [cmake-use-pragma-once]
+int neither()
+^
+cmake-use-pragma-once/cmake-use-pragma-once-neither.h:1:1: note: FIX-IT applied suggested code changes
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once.cxx b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once.cxx
new file mode 100644
index 0000000000..a571bc1137
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once.cxx
@@ -0,0 +1,5 @@
+#include "cmake-use-pragma-once/cmake-use-pragma-once.h"
+
+#include "cmake-use-pragma-once/cmake-use-pragma-once-both.h"
+#include "cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h"
+#include "cmake-use-pragma-once/cmake-use-pragma-once-neither.h"
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-both-fixit.h b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-both-fixit.h
new file mode 100644
index 0000000000..73c9720d73
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-both-fixit.h
@@ -0,0 +1,8 @@
+
+
+#pragma once
+
+int both()
+{
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-both.h b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-both.h
new file mode 100644
index 0000000000..fdf3cd3edc
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-both.h
@@ -0,0 +1,10 @@
+#ifndef BOTH_H
+#define BOTH_H
+#pragma once
+
+int both()
+{
+ return 0;
+}
+
+#endif
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-include-guards-fixit.h b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-include-guards-fixit.h
new file mode 100644
index 0000000000..36461c24f0
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-include-guards-fixit.h
@@ -0,0 +1,6 @@
+#pragma once
+
+int includeGuards()
+{
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h
new file mode 100644
index 0000000000..687306db33
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-include-guards.h
@@ -0,0 +1,9 @@
+#ifndef INCLUDE_GUARDS_H
+#define INCLUDE_GUARDS_H
+
+int includeGuards()
+{
+ return 0;
+}
+
+#endif
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-neither-fixit.h b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-neither-fixit.h
new file mode 100644
index 0000000000..eb5c6dd2e2
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-neither-fixit.h
@@ -0,0 +1,5 @@
+#pragma once
+int neither()
+{
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-neither.h b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-neither.h
new file mode 100644
index 0000000000..c779ca0b58
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once-neither.h
@@ -0,0 +1,4 @@
+int neither()
+{
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once.h b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once.h
new file mode 100644
index 0000000000..b0b2ea2313
--- /dev/null
+++ b/Utilities/ClangTidyModule/Tests/cmake-use-pragma-once/cmake-use-pragma-once.h
@@ -0,0 +1,6 @@
+#pragma once
+
+int once()
+{
+ return 0;
+}
diff --git a/Utilities/ClangTidyModule/UseBespokeEnumClassCheck.cxx b/Utilities/ClangTidyModule/UseBespokeEnumClassCheck.cxx
new file mode 100644
index 0000000000..26f3749374
--- /dev/null
+++ b/Utilities/ClangTidyModule/UseBespokeEnumClassCheck.cxx
@@ -0,0 +1,35 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "UseBespokeEnumClassCheck.h"
+
+#include <clang/AST/Type.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+using namespace ast_matchers;
+
+UseBespokeEnumClassCheck::UseBespokeEnumClassCheck(StringRef Name,
+ ClangTidyContext* Context)
+ : ClangTidyCheck(Name, Context)
+{
+}
+
+void UseBespokeEnumClassCheck::registerMatchers(MatchFinder* Finder)
+{
+ Finder->addMatcher(
+ parmVarDecl(
+ hasTypeLoc(typeLoc(loc(qualType(asString("_Bool")))).bind("type"))),
+ this);
+}
+
+void UseBespokeEnumClassCheck::check(const MatchFinder::MatchResult& Result)
+{
+ const TypeLoc* Node = Result.Nodes.getNodeAs<TypeLoc>("type");
+ this->diag(Node->getBeginLoc(),
+ "use a bespoke enum class instead of booleans as parameters");
+}
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/UseBespokeEnumClassCheck.h b/Utilities/ClangTidyModule/UseBespokeEnumClassCheck.h
new file mode 100644
index 0000000000..be76db037e
--- /dev/null
+++ b/Utilities/ClangTidyModule/UseBespokeEnumClassCheck.h
@@ -0,0 +1,21 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <clang-tidy/ClangTidyCheck.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class UseBespokeEnumClassCheck : public ClangTidyCheck
+{
+public:
+ UseBespokeEnumClassCheck(StringRef Name, ClangTidyContext* Context);
+ void registerMatchers(ast_matchers::MatchFinder* Finder) override;
+
+ void check(const ast_matchers::MatchFinder::MatchResult& Result) override;
+};
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/UseCmstrlenCheck.cxx b/Utilities/ClangTidyModule/UseCmstrlenCheck.cxx
new file mode 100644
index 0000000000..d4bae1f794
--- /dev/null
+++ b/Utilities/ClangTidyModule/UseCmstrlenCheck.cxx
@@ -0,0 +1,78 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "UseCmstrlenCheck.h"
+
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+using namespace ast_matchers;
+
+UseCmstrlenCheck::UseCmstrlenCheck(StringRef Name, ClangTidyContext* Context)
+ : ClangTidyCheck(Name, Context)
+{
+}
+
+void UseCmstrlenCheck::registerMatchers(MatchFinder* Finder)
+{
+ Finder->addMatcher(callExpr(callee(functionDecl(hasName("::strlen"))),
+ callee(expr().bind("strlen")),
+ hasArgument(0, stringLiteral())),
+ this);
+
+ auto IsSizeOfStringLiteral =
+ unaryExprOrTypeTraitExpr(
+ ofKind(UETT_SizeOf),
+ anyOf(has(parenExpr(has(stringLiteral())).bind("paren")),
+ has(stringLiteral())))
+ .bind("sizeOf");
+ Finder->addMatcher(
+ binaryOperator(
+ hasOperatorName("-"),
+ hasLHS(anyOf(
+ binaryOperator(hasOperatorName("+"), hasRHS(IsSizeOfStringLiteral)),
+ IsSizeOfStringLiteral)),
+ hasRHS(implicitCastExpr(has(integerLiteral(equals(1)).bind("literal")))))
+ .bind("sizeOfMinus"),
+ this);
+}
+
+void UseCmstrlenCheck::check(const MatchFinder::MatchResult& Result)
+{
+ const Expr* Strlen = Result.Nodes.getNodeAs<Expr>("strlen");
+ const BinaryOperator* SizeOfMinus =
+ Result.Nodes.getNodeAs<BinaryOperator>("sizeOfMinus");
+
+ if (Strlen) {
+ this->diag(Strlen->getBeginLoc(), "use cmStrLen() for string literals")
+ << FixItHint::CreateReplacement(Strlen->getSourceRange(), "cmStrLen");
+ }
+
+ if (SizeOfMinus) {
+ const ParenExpr* Paren = Result.Nodes.getNodeAs<ParenExpr>("paren");
+ const UnaryExprOrTypeTraitExpr* SizeOf =
+ Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeOf");
+ const IntegerLiteral* Literal =
+ Result.Nodes.getNodeAs<IntegerLiteral>("literal");
+
+ std::vector<FixItHint> FixIts;
+ if (Paren) {
+ FixIts.push_back(
+ FixItHint::CreateReplacement(SizeOf->getOperatorLoc(), "cmStrLen"));
+ FixIts.push_back(FixItHint::CreateRemoval(
+ SourceRange(SizeOfMinus->getOperatorLoc(), Literal->getLocation())));
+ } else {
+ FixIts.push_back(
+ FixItHint::CreateReplacement(SizeOf->getOperatorLoc(), "cmStrLen("));
+ FixIts.push_back(FixItHint::CreateReplacement(
+ SourceRange(SizeOfMinus->getOperatorLoc(), Literal->getLocation()),
+ ")"));
+ }
+ this->diag(SizeOf->getOperatorLoc(), "use cmStrLen() for string literals")
+ << FixIts;
+ }
+}
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/UseCmstrlenCheck.h b/Utilities/ClangTidyModule/UseCmstrlenCheck.h
new file mode 100644
index 0000000000..08f77c23a9
--- /dev/null
+++ b/Utilities/ClangTidyModule/UseCmstrlenCheck.h
@@ -0,0 +1,21 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <clang-tidy/ClangTidyCheck.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class UseCmstrlenCheck : public ClangTidyCheck
+{
+public:
+ UseCmstrlenCheck(StringRef Name, ClangTidyContext* Context);
+ void registerMatchers(ast_matchers::MatchFinder* Finder) override;
+
+ void check(const ast_matchers::MatchFinder::MatchResult& Result) override;
+};
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx b/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx
new file mode 100644
index 0000000000..95a0a4d6fa
--- /dev/null
+++ b/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx
@@ -0,0 +1,101 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "UseCmsysFstreamCheck.h"
+
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+using namespace ast_matchers;
+
+UseCmsysFstreamCheck::UseCmsysFstreamCheck(StringRef Name,
+ ClangTidyContext* Context)
+ : ClangTidyCheck(Name, Context)
+{
+}
+
+void UseCmsysFstreamCheck::registerMatchers(MatchFinder* Finder)
+{
+ this->createMatcher("::std::basic_ifstream", "::cmsys::ifstream", Finder,
+ "ifstream");
+ this->createMatcher("::std::basic_ofstream", "::cmsys::ofstream", Finder,
+ "ofstream");
+ this->createMatcher("::std::basic_fstream", "::cmsys::fstream", Finder,
+ "fstream");
+}
+
+void UseCmsysFstreamCheck::check(const MatchFinder::MatchResult& Result)
+{
+ const TypeLoc* ParentTypeNode =
+ Result.Nodes.getNodeAs<TypeLoc>("parentType");
+ const NestedNameSpecifierLoc* ParentNameNode =
+ Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("parentName");
+ const TypeLoc* RootNode = nullptr;
+ StringRef BindName;
+ StringRef Warning;
+
+ if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("ifstream")) != nullptr) {
+ BindName = "cmsys::ifstream";
+ Warning = "use cmsys::ifstream";
+ } else if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("ofstream")) !=
+ nullptr) {
+ BindName = "cmsys::ofstream";
+ Warning = "use cmsys::ofstream";
+ } else if ((RootNode = Result.Nodes.getNodeAs<TypeLoc>("fstream")) !=
+ nullptr) {
+ BindName = "cmsys::fstream";
+ Warning = "use cmsys::fstream";
+ }
+
+ if (ParentTypeNode != nullptr) {
+ if (ParentTypeNode->getBeginLoc().isValid()) {
+ this->diag(ParentTypeNode->getBeginLoc(), Warning)
+ << FixItHint::CreateReplacement(ParentTypeNode->getSourceRange(),
+ BindName);
+ }
+ } else if (ParentNameNode != nullptr) {
+ if (ParentNameNode->getBeginLoc().isValid()) {
+ this->diag(ParentNameNode->getBeginLoc(), Warning)
+ << FixItHint::CreateReplacement(
+ SourceRange(ParentNameNode->getBeginLoc(), RootNode->getEndLoc()),
+ BindName);
+ }
+ } else if (RootNode != nullptr) {
+ if (RootNode->getBeginLoc().isValid()) {
+ this->diag(RootNode->getBeginLoc(), Warning)
+ << FixItHint::CreateReplacement(RootNode->getSourceRange(), BindName);
+ }
+ }
+}
+
+void UseCmsysFstreamCheck::createMatcher(StringRef StdName,
+ StringRef CmsysName,
+ ast_matchers::MatchFinder* Finder,
+ StringRef Bind)
+{
+ TypeLocMatcher IsStd = loc(qualType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(classTemplateSpecializationDecl(
+ hasName(StdName),
+ hasTemplateArgument(
+ 0, templateArgument(refersToType(asString("char"))))))))));
+
+ // TODO This only checks to see if the type directly refers to
+ // cmsys::fstream. There are some corner cases involving template parameters
+ // that refer to cmsys::fstream that are missed by this matcher, resulting in
+ // a false positive. Figure out how to find these indirect references to
+ // cmsys::fstream and filter them out. In the meantime, such false positives
+ // can be silenced with NOLINT(cmake-use-cmsys-fstream).
+ TypeLocMatcher IsCmsys =
+ loc(usingType(throughUsingDecl(namedDecl(hasName(CmsysName)))));
+
+ Finder->addMatcher(
+ typeLoc(IsStd, unless(IsCmsys), unless(elaboratedTypeLoc()),
+ optionally(hasParent(elaboratedTypeLoc().bind("parentType"))),
+ optionally(hasParent(nestedNameSpecifierLoc().bind("parentName"))))
+ .bind(Bind),
+ this);
+}
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/UseCmsysFstreamCheck.h b/Utilities/ClangTidyModule/UseCmsysFstreamCheck.h
new file mode 100644
index 0000000000..782123c12a
--- /dev/null
+++ b/Utilities/ClangTidyModule/UseCmsysFstreamCheck.h
@@ -0,0 +1,24 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <clang-tidy/ClangTidyCheck.h>
+#include <clang/ASTMatchers/ASTMatchFinder.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+class UseCmsysFstreamCheck : public ClangTidyCheck
+{
+public:
+ UseCmsysFstreamCheck(StringRef Name, ClangTidyContext* Context);
+ void registerMatchers(ast_matchers::MatchFinder* Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult& Result) override;
+
+private:
+ void createMatcher(StringRef name, StringRef CmsysName,
+ ast_matchers::MatchFinder* Finder, StringRef bind);
+};
+}
+}
+}
diff --git a/Utilities/ClangTidyModule/UsePragmaOnceCheck.cxx b/Utilities/ClangTidyModule/UsePragmaOnceCheck.cxx
new file mode 100644
index 0000000000..7a42798a6d
--- /dev/null
+++ b/Utilities/ClangTidyModule/UsePragmaOnceCheck.cxx
@@ -0,0 +1,325 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+/* This code was originally taken from part of the Clang-Tidy LLVM project and
+ * modified for use with CMake under the following original license: */
+
+//===--- HeaderGuard.cpp - clang-tidy
+//-------------------------------------===//
+//
+// 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 "UsePragmaOnceCheck.h"
+
+#include <algorithm>
+#include <cassert>
+
+#include <clang/Frontend/CompilerInstance.h>
+#include <clang/Lex/PPCallbacks.h>
+#include <clang/Lex/Preprocessor.h>
+#include <clang/Tooling/Tooling.h>
+#include <llvm/Support/Path.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+
+/// canonicalize a path by removing ./ and ../ components.
+static std::string cleanPath(StringRef Path)
+{
+ SmallString<256> Result = Path;
+ llvm::sys::path::remove_dots(Result, true);
+ return std::string(Result.str());
+}
+
+namespace {
+// This class is a workaround for the fact that PPCallbacks doesn't give us the
+// location of the hash for an #ifndef, #define, or #endif, so we have to find
+// it ourselves. We can't lex backwards, and attempting to turn on the
+// preprocessor's backtracking functionality wreaks havoc, so we have to
+// instantiate a second lexer and lex all the way from the beginning of the
+// file. Cache the results of this lexing so that we don't have to do it more
+// times than needed.
+//
+// TODO: Upstream a change to LLVM to give us the location of the hash in
+// PPCallbacks so we don't have to do this workaround.
+class DirectiveCache
+{
+public:
+ DirectiveCache(Preprocessor* PP, FileID FID)
+ : PP(PP)
+ , FID(FID)
+ {
+ SourceManager& SM = this->PP->getSourceManager();
+ const FileEntry* Entry = SM.getFileEntryForID(FID);
+ assert(Entry && "Invalid FileID given");
+
+ Lexer MyLexer(FID, SM.getMemoryBufferForFileOrFake(Entry), SM,
+ this->PP->getLangOpts());
+ Token Tok;
+
+ while (!MyLexer.LexFromRawLexer(Tok)) {
+ if (Tok.getKind() == tok::hash) {
+ assert(SM.getFileID(Tok.getLocation()) == this->FID &&
+ "Token FileID does not match passed FileID");
+ if (!this->HashLocs.empty()) {
+ assert(SM.getFileOffset(this->HashLocs.back()) <
+ SM.getFileOffset(Tok.getLocation()) &&
+ "Tokens in file are not in order");
+ }
+
+ this->HashLocs.push_back(Tok.getLocation());
+ }
+ }
+ }
+
+ SourceRange createRangeForIfndef(SourceLocation IfndefMacroTokLoc)
+ {
+ // The #ifndef of an include guard is likely near the beginning of the
+ // file, so search from the front.
+ return SourceRange(this->findPreviousHashFromFront(IfndefMacroTokLoc),
+ IfndefMacroTokLoc);
+ }
+
+ SourceRange createRangeForDefine(SourceLocation DefineMacroTokLoc)
+ {
+ // The #define of an include guard is likely near the beginning of the
+ // file, so search from the front.
+ return SourceRange(this->findPreviousHashFromFront(DefineMacroTokLoc),
+ DefineMacroTokLoc);
+ }
+
+ SourceRange createRangeForEndif(SourceLocation EndifLoc)
+ {
+ // The #endif of an include guard is likely near the end of the file, so
+ // search from the back.
+ return SourceRange(this->findPreviousHashFromBack(EndifLoc), EndifLoc);
+ }
+
+private:
+ Preprocessor* PP;
+ FileID FID;
+ SmallVector<SourceLocation> HashLocs;
+
+ SourceLocation findPreviousHashFromFront(SourceLocation Loc)
+ {
+ SourceManager& SM = this->PP->getSourceManager();
+ Loc = SM.getExpansionLoc(Loc);
+ assert(SM.getFileID(Loc) == this->FID &&
+ "Loc FileID does not match our FileID");
+
+ auto It = std::find_if(
+ this->HashLocs.begin(), this->HashLocs.end(),
+ [&SM, &Loc](const SourceLocation& OtherLoc) -> bool {
+ return SM.getFileOffset(OtherLoc) >= SM.getFileOffset(Loc);
+ });
+ assert(It != this->HashLocs.begin() &&
+ "No hash associated with passed Loc");
+ return *--It;
+ }
+
+ SourceLocation findPreviousHashFromBack(SourceLocation Loc)
+ {
+ SourceManager& SM = this->PP->getSourceManager();
+ Loc = SM.getExpansionLoc(Loc);
+ assert(SM.getFileID(Loc) == this->FID &&
+ "Loc FileID does not match our FileID");
+
+ auto It =
+ std::find_if(this->HashLocs.rbegin(), this->HashLocs.rend(),
+ [&SM, &Loc](const SourceLocation& OtherLoc) -> bool {
+ return SM.getFileOffset(OtherLoc) < SM.getFileOffset(Loc);
+ });
+ assert(It != this->HashLocs.rend() &&
+ "No hash associated with passed Loc");
+ return *It;
+ }
+};
+
+class UsePragmaOncePPCallbacks : public PPCallbacks
+{
+public:
+ UsePragmaOncePPCallbacks(Preprocessor* PP, UsePragmaOnceCheck* Check)
+ : PP(PP)
+ , Check(Check)
+ {
+ }
+
+ void FileChanged(SourceLocation Loc, FileChangeReason Reason,
+ SrcMgr::CharacteristicKind FileType,
+ FileID PrevFID) override
+ {
+ // Record all files we enter. We'll need them to diagnose headers without
+ // guards.
+ SourceManager& SM = this->PP->getSourceManager();
+ if (Reason == EnterFile && FileType == SrcMgr::C_User) {
+ if (const FileEntry* FE = SM.getFileEntryForID(SM.getFileID(Loc))) {
+ std::string FileName = cleanPath(FE->getName());
+ this->Files[FileName] = FE;
+ }
+ }
+ }
+
+ void Ifndef(SourceLocation Loc, const Token& MacroNameTok,
+ const MacroDefinition& MD) override
+ {
+ if (MD) {
+ return;
+ }
+
+ // Record #ifndefs that succeeded. We also need the Location of the Name.
+ this->Ifndefs[MacroNameTok.getIdentifierInfo()] =
+ std::make_pair(Loc, MacroNameTok.getLocation());
+ }
+
+ void MacroDefined(const Token& MacroNameTok,
+ const MacroDirective* MD) override
+ {
+ // Record all defined macros. We store the whole token to get info on the
+ // name later.
+ this->Macros.emplace_back(MacroNameTok, MD->getMacroInfo());
+ }
+
+ void Endif(SourceLocation Loc, SourceLocation IfLoc) override
+ {
+ // Record all #endif and the corresponding #ifs (including #ifndefs).
+ this->EndIfs[IfLoc] = Loc;
+ }
+
+ void EndOfMainFile() override
+ {
+ // Now that we have all this information from the preprocessor, use it!
+ SourceManager& SM = this->PP->getSourceManager();
+
+ for (const auto& MacroEntry : this->Macros) {
+ const MacroInfo* MI = MacroEntry.second;
+
+ // We use clang's header guard detection. This has the advantage of also
+ // emitting a warning for cases where a pseudo header guard is found but
+ // preceded by something blocking the header guard optimization.
+ if (!MI->isUsedForHeaderGuard()) {
+ continue;
+ }
+
+ const FileEntry* FE =
+ SM.getFileEntryForID(SM.getFileID(MI->getDefinitionLoc()));
+ std::string FileName = cleanPath(FE->getName());
+ this->Files.erase(FileName);
+
+ // Look up Locations for this guard.
+ SourceLocation Ifndef =
+ this->Ifndefs[MacroEntry.first.getIdentifierInfo()].second;
+ SourceLocation Define = MacroEntry.first.getLocation();
+ SourceLocation EndIf =
+ this
+ ->EndIfs[this->Ifndefs[MacroEntry.first.getIdentifierInfo()].first];
+
+ StringRef CurHeaderGuard =
+ MacroEntry.first.getIdentifierInfo()->getName();
+ std::vector<FixItHint> FixIts;
+
+ HeaderSearch& HeaderInfo = this->PP->getHeaderSearchInfo();
+
+ HeaderFileInfo& Info = HeaderInfo.getFileInfo(FE);
+
+ DirectiveCache Cache(this->PP, SM.getFileID(MI->getDefinitionLoc()));
+ SourceRange IfndefSrcRange = Cache.createRangeForIfndef(Ifndef);
+ SourceRange DefineSrcRange = Cache.createRangeForDefine(Define);
+ SourceRange EndifSrcRange = Cache.createRangeForEndif(EndIf);
+
+ if (Info.isPragmaOnce) {
+ FixIts.push_back(FixItHint::CreateRemoval(IfndefSrcRange));
+ } else {
+ FixIts.push_back(
+ FixItHint::CreateReplacement(IfndefSrcRange, "#pragma once"));
+ }
+
+ FixIts.push_back(FixItHint::CreateRemoval(DefineSrcRange));
+ FixIts.push_back(FixItHint::CreateRemoval(EndifSrcRange));
+
+ this->Check->diag(IfndefSrcRange.getBegin(), "use #pragma once")
+ << FixIts;
+ }
+
+ // Emit warnings for headers that are missing guards.
+ checkGuardlessHeaders();
+ clearAllState();
+ }
+
+ /// Looks for files that were visited but didn't have a header guard.
+ /// Emits a warning with fixits suggesting adding one.
+ void checkGuardlessHeaders()
+ {
+ // Look for header files that didn't have a header guard. Emit a warning
+ // and fix-its to add the guard.
+ // TODO: Insert the guard after top comments.
+ for (const auto& FE : this->Files) {
+ StringRef FileName = FE.getKey();
+ if (!Check->shouldSuggestToAddPragmaOnce(FileName)) {
+ continue;
+ }
+
+ SourceManager& SM = this->PP->getSourceManager();
+ FileID FID = SM.translateFile(FE.getValue());
+ SourceLocation StartLoc = SM.getLocForStartOfFile(FID);
+ if (StartLoc.isInvalid()) {
+ continue;
+ }
+
+ HeaderSearch& HeaderInfo = this->PP->getHeaderSearchInfo();
+
+ HeaderFileInfo& Info = HeaderInfo.getFileInfo(FE.second);
+ if (Info.isPragmaOnce) {
+ continue;
+ }
+
+ this->Check->diag(StartLoc, "use #pragma once")
+ << FixItHint::CreateInsertion(StartLoc, "#pragma once\n");
+ }
+ }
+
+private:
+ void clearAllState()
+ {
+ this->Macros.clear();
+ this->Files.clear();
+ this->Ifndefs.clear();
+ this->EndIfs.clear();
+ }
+
+ std::vector<std::pair<Token, const MacroInfo*>> Macros;
+ llvm::StringMap<const FileEntry*> Files;
+ std::map<const IdentifierInfo*, std::pair<SourceLocation, SourceLocation>>
+ Ifndefs;
+ std::map<SourceLocation, SourceLocation> EndIfs;
+
+ Preprocessor* PP;
+ UsePragmaOnceCheck* Check;
+};
+} // namespace
+
+void UsePragmaOnceCheck::storeOptions(ClangTidyOptions::OptionMap& Opts)
+{
+ this->Options.store(Opts, "HeaderFileExtensions",
+ RawStringHeaderFileExtensions);
+}
+
+void UsePragmaOnceCheck::registerPPCallbacks(const SourceManager& SM,
+ Preprocessor* PP,
+ Preprocessor* ModuleExpanderPP)
+{
+ PP->addPPCallbacks(std::make_unique<UsePragmaOncePPCallbacks>(PP, this));
+}
+
+bool UsePragmaOnceCheck::shouldSuggestToAddPragmaOnce(StringRef FileName)
+{
+ return utils::isFileExtension(FileName, this->HeaderFileExtensions);
+}
+
+} // namespace cmake
+} // namespace tidy
+} // namespace clang
diff --git a/Utilities/ClangTidyModule/UsePragmaOnceCheck.h b/Utilities/ClangTidyModule/UsePragmaOnceCheck.h
new file mode 100644
index 0000000000..08c2099a9f
--- /dev/null
+++ b/Utilities/ClangTidyModule/UsePragmaOnceCheck.h
@@ -0,0 +1,60 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+/* This code was originally taken from part of the Clang-Tidy LLVM project and
+ * modified for use with CMake under the following original license: */
+
+//===--- HeaderGuard.h - clang-tidy -----------------------------*- C++
+//-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include <clang-tidy/ClangTidyCheck.h>
+#include <clang-tidy/utils/FileExtensionsUtils.h>
+
+namespace clang {
+namespace tidy {
+namespace cmake {
+
+/// Finds and replaces header guards with pragma once.
+/// The check supports these options:
+/// - `HeaderFileExtensions`: a semicolon-separated list of filename
+/// extensions of header files (The filename extension should not contain
+/// "." prefix). ";h;hh;hpp;hxx" by default.
+///
+/// For extension-less header files, using an empty string or leaving an
+/// empty string between ";" if there are other filename extensions.
+class UsePragmaOnceCheck : public ClangTidyCheck
+{
+public:
+ UsePragmaOnceCheck(StringRef Name, ClangTidyContext* Context)
+ : ClangTidyCheck(Name, Context)
+ , RawStringHeaderFileExtensions(Options.getLocalOrGlobal(
+ "HeaderFileExtensions", utils::defaultHeaderFileExtensions()))
+ {
+ utils::parseFileExtensions(RawStringHeaderFileExtensions,
+ HeaderFileExtensions,
+ utils::defaultFileExtensionDelimiters());
+ }
+ void storeOptions(ClangTidyOptions::OptionMap& Opts) override;
+ void registerPPCallbacks(const SourceManager& SM, Preprocessor* PP,
+ Preprocessor* ModuleExpanderPP) override;
+
+ /// Returns ``true`` if the check should add pragma once to the file
+ /// if it has none.
+ virtual bool shouldSuggestToAddPragmaOnce(StringRef Filename);
+
+private:
+ std::string RawStringHeaderFileExtensions;
+ utils::FileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace cmake
+} // namespace tidy
+} // namespace clang