317 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include "ClangTidyOptions.h"
 | |
| #include "ClangTidyTest.h"
 | |
| #include "llvm/ADT/ArrayRef.h"
 | |
| #include "llvm/ADT/StringRef.h"
 | |
| #include "llvm/HeaderGuardCheck.h"
 | |
| #include "llvm/IncludeOrderCheck.h"
 | |
| #include "gtest/gtest.h"
 | |
| 
 | |
| using namespace clang::tidy::llvm_check;
 | |
| 
 | |
| namespace clang {
 | |
| namespace tidy {
 | |
| namespace test {
 | |
| 
 | |
| template <typename T>
 | |
| static std::string runCheck(StringRef Code, const Twine &Filename,
 | |
|                             Optional<StringRef> ExpectedWarning,
 | |
|                             std::map<StringRef, StringRef> PathsToContent =
 | |
|                                 std::map<StringRef, StringRef>()) {
 | |
|   std::vector<ClangTidyError> Errors;
 | |
|   std::string Result = test::runCheckOnCode<T>(
 | |
|       Code, &Errors, Filename, std::string("-xc++-header"), ClangTidyOptions{},
 | |
|       std::move(PathsToContent));
 | |
|   if (Errors.size() != (size_t)ExpectedWarning.has_value())
 | |
|     return "invalid error count";
 | |
|   if (ExpectedWarning && *ExpectedWarning != Errors.back().Message.Message)
 | |
|     return "expected: '" + ExpectedWarning->str() + "', saw: '" +
 | |
|            Errors.back().Message.Message + "'";
 | |
|   return Result;
 | |
| }
 | |
| 
 | |
| static std::string runHeaderGuardCheck(StringRef Code, const Twine &Filename,
 | |
|                                        Optional<StringRef> ExpectedWarning) {
 | |
|   return runCheck<LLVMHeaderGuardCheck>(Code, Filename,
 | |
|                                         std::move(ExpectedWarning));
 | |
| }
 | |
| 
 | |
| static std::string
 | |
| runIncludeOrderCheck(StringRef Code, const Twine &Filename,
 | |
|                      Optional<StringRef> ExpectedWarning,
 | |
|                      llvm::ArrayRef<llvm::StringLiteral> Includes) {
 | |
|   std::map<StringRef, StringRef> PathsToContent;
 | |
|   for (auto Include : Includes)
 | |
|     PathsToContent.emplace(Include, "");
 | |
|   return runCheck<IncludeOrderCheck>(Code, Filename, std::move(ExpectedWarning),
 | |
|                                      PathsToContent);
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| struct WithEndifComment : public LLVMHeaderGuardCheck {
 | |
|   WithEndifComment(StringRef Name, ClangTidyContext *Context)
 | |
|       : LLVMHeaderGuardCheck(Name, Context) {}
 | |
|   bool shouldSuggestEndifComment(StringRef Filename) override { return true; }
 | |
| };
 | |
| 
 | |
| static std::string
 | |
| runHeaderGuardCheckWithEndif(StringRef Code, const Twine &Filename,
 | |
|                              Optional<StringRef> ExpectedWarning) {
 | |
|   return runCheck<WithEndifComment>(Code, Filename, std::move(ExpectedWarning));
 | |
| }
 | |
| } // namespace
 | |
| 
 | |
| TEST(LLVMHeaderGuardCheckTest, FixHeaderGuards) {
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
 | |
|             "#define LLVM_ADT_FOO_H\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck(
 | |
|                 "#ifndef FOO\n"
 | |
|                 "#define FOO\n"
 | |
|                 "#endif\n",
 | |
|                 "include/llvm/ADT/foo.h",
 | |
|                 StringRef("header guard does not follow preferred style")));
 | |
| 
 | |
|   // Allow trailing underscores.
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H_\n"
 | |
|             "#define LLVM_ADT_FOO_H_\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck("#ifndef LLVM_ADT_FOO_H_\n"
 | |
|                                 "#define LLVM_ADT_FOO_H_\n"
 | |
|                                 "#endif\n",
 | |
|                                 "include/llvm/ADT/foo.h", None));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_CLANG_C_BAR_H\n"
 | |
|             "#define LLVM_CLANG_C_BAR_H\n"
 | |
|             "\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck("", "./include/clang-c/bar.h",
 | |
|                                 StringRef("header is missing header guard")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_CLANG_LIB_CODEGEN_C_H\n"
 | |
|             "#define LLVM_CLANG_LIB_CODEGEN_C_H\n"
 | |
|             "\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck("", "tools/clang/lib/CodeGen/c.h",
 | |
|                                 StringRef("header is missing header guard")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_X_H\n"
 | |
|             "#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_X_H\n"
 | |
|             "\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck("", "tools/clang/tools/extra/clang-tidy/x.h",
 | |
|                                 StringRef("header is missing header guard")));
 | |
| 
 | |
|   EXPECT_EQ(
 | |
|       "int foo;\n"
 | |
|       "#ifndef LLVM_CLANG_BAR_H\n"
 | |
|       "#define LLVM_CLANG_BAR_H\n"
 | |
|       "#endif\n",
 | |
|       runHeaderGuardCheck("int foo;\n"
 | |
|                           "#ifndef LLVM_CLANG_BAR_H\n"
 | |
|                           "#define LLVM_CLANG_BAR_H\n"
 | |
|                           "#endif\n",
 | |
|                           "include/clang/bar.h",
 | |
|                           StringRef("code/includes outside of area guarded by "
 | |
|                                     "header guard; consider moving it")));
 | |
| 
 | |
|   EXPECT_EQ(
 | |
|       "#ifndef LLVM_CLANG_BAR_H\n"
 | |
|       "#define LLVM_CLANG_BAR_H\n"
 | |
|       "#endif\n"
 | |
|       "int foo;\n",
 | |
|       runHeaderGuardCheck("#ifndef LLVM_CLANG_BAR_H\n"
 | |
|                           "#define LLVM_CLANG_BAR_H\n"
 | |
|                           "#endif\n"
 | |
|                           "int foo;\n",
 | |
|                           "include/clang/bar.h",
 | |
|                           StringRef("code/includes outside of area guarded by "
 | |
|                                     "header guard; consider moving it")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_CLANG_BAR_H\n"
 | |
|             "#define LLVM_CLANG_BAR_H\n"
 | |
|             "\n"
 | |
|             "int foo;\n"
 | |
|             "#ifndef FOOLOLO\n"
 | |
|             "#define FOOLOLO\n"
 | |
|             "#endif\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck("int foo;\n"
 | |
|                                 "#ifndef FOOLOLO\n"
 | |
|                                 "#define FOOLOLO\n"
 | |
|                                 "#endif\n",
 | |
|                                 "include/clang/bar.h",
 | |
|                                 StringRef("header is missing header guard")));
 | |
| 
 | |
|   // Fix incorrect #endif comments even if we shouldn't add new ones.
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
 | |
|             "#define LLVM_ADT_FOO_H\n"
 | |
|             "#endif // LLVM_ADT_FOO_H\n",
 | |
|             runHeaderGuardCheck(
 | |
|                 "#ifndef FOO\n"
 | |
|                 "#define FOO\n"
 | |
|                 "#endif // FOO\n",
 | |
|                 "include/llvm/ADT/foo.h",
 | |
|                 StringRef("header guard does not follow preferred style")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
 | |
|             "#define LLVM_ADT_FOO_H\n"
 | |
|             "#endif // LLVM_ADT_FOO_H\n",
 | |
|             runHeaderGuardCheckWithEndif(
 | |
|                 "#ifndef FOO\n"
 | |
|                 "#define FOO\n"
 | |
|                 "#endif\n",
 | |
|                 "include/llvm/ADT/foo.h",
 | |
|                 StringRef("header guard does not follow preferred style")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
 | |
|             "#define LLVM_ADT_FOO_H\n"
 | |
|             "#endif // LLVM_ADT_FOO_H\n",
 | |
|             runHeaderGuardCheckWithEndif(
 | |
|                 "#ifndef LLVM_ADT_FOO_H\n"
 | |
|                 "#define LLVM_ADT_FOO_H\n"
 | |
|                 "#endif // LLVM_H\n",
 | |
|                 "include/llvm/ADT/foo.h",
 | |
|                 StringRef("#endif for a header guard should reference the "
 | |
|                           "guard macro in a comment")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
 | |
|             "#define LLVM_ADT_FOO_H\n"
 | |
|             "#endif /* LLVM_ADT_FOO_H */\n",
 | |
|             runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H\n"
 | |
|                                          "#define LLVM_ADT_FOO_H\n"
 | |
|                                          "#endif /* LLVM_ADT_FOO_H */\n",
 | |
|                                          "include/llvm/ADT/foo.h", None));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H_\n"
 | |
|             "#define LLVM_ADT_FOO_H_\n"
 | |
|             "#endif // LLVM_ADT_FOO_H_\n",
 | |
|             runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H_\n"
 | |
|                                          "#define LLVM_ADT_FOO_H_\n"
 | |
|                                          "#endif // LLVM_ADT_FOO_H_\n",
 | |
|                                          "include/llvm/ADT/foo.h", None));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
 | |
|             "#define LLVM_ADT_FOO_H\n"
 | |
|             "#endif // LLVM_ADT_FOO_H\n",
 | |
|             runHeaderGuardCheckWithEndif(
 | |
|                 "#ifndef LLVM_ADT_FOO_H_\n"
 | |
|                 "#define LLVM_ADT_FOO_H_\n"
 | |
|                 "#endif // LLVM\n",
 | |
|                 "include/llvm/ADT/foo.h",
 | |
|                 StringRef("header guard does not follow preferred style")));
 | |
| 
 | |
|   // An extra space inside the comment is OK.
 | |
|   llvm::StringRef WithExtraSpace = "#ifndef LLVM_ADT_FOO_H\n"
 | |
|                                    "#define LLVM_ADT_FOO_H\n"
 | |
|                                    "#endif //  LLVM_ADT_FOO_H\n";
 | |
|   EXPECT_EQ(WithExtraSpace,
 | |
|             runHeaderGuardCheckWithEndif(WithExtraSpace,
 | |
|                                          "include/llvm/ADT/foo.h", None));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
 | |
|             "#define LLVM_ADT_FOO_H\n"
 | |
|             "#endif \\ \n"
 | |
|             "// LLVM_ADT_FOO_H\n",
 | |
|             runHeaderGuardCheckWithEndif(
 | |
|                 "#ifndef LLVM_ADT_FOO_H\n"
 | |
|                 "#define LLVM_ADT_FOO_H\n"
 | |
|                 "#endif \\ \n"
 | |
|                 "// LLVM_ADT_FOO_H\n",
 | |
|                 "include/llvm/ADT/foo.h",
 | |
|                 StringRef("backslash and newline separated by space")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
 | |
|             "#define LLVM_ADT_FOO_H\n"
 | |
|             "#endif  /* LLVM_ADT_FOO_H\\ \n"
 | |
|             " FOO */",
 | |
|             runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H\n"
 | |
|                                          "#define LLVM_ADT_FOO_H\n"
 | |
|                                          "#endif  /* LLVM_ADT_FOO_H\\ \n"
 | |
|                                          " FOO */",
 | |
|                                          "include/llvm/ADT/foo.h", None));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|             "#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|             "\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck(
 | |
|                 "", "/llvm-project/clang-tools-extra/clangd/foo.h",
 | |
|                 StringRef("header is missing header guard")));
 | |
| 
 | |
|   // Substitution of characters should not result in a header guard starting
 | |
|   // with "_".
 | |
|   EXPECT_EQ("#ifndef BAR_H\n"
 | |
|             "#define BAR_H\n"
 | |
|             "\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck("", "include/--bar.h",
 | |
|                                 StringRef("header is missing header guard")));
 | |
| 
 | |
| #ifdef WIN32
 | |
|   // Check interaction with Windows-style path separators (\).
 | |
|   EXPECT_EQ(
 | |
|       "#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|       "#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|       "\n"
 | |
|       "\n"
 | |
|       "#endif\n",
 | |
|       runHeaderGuardCheck("", "llvm-project\\clang-tools-extra\\clangd\\foo.h",
 | |
|                           StringRef("header is missing header guard")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|             "#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|             "\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck(
 | |
|                 "", "C:\\llvm-project\\clang-tools-extra\\clangd\\foo.h",
 | |
|                 StringRef("header is missing header guard")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|             "#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|             "\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck(
 | |
|                 "",
 | |
|                 "\\\\SMBShare\\llvm-project\\clang-tools-extra\\clangd\\foo.h",
 | |
|                 StringRef("header is missing header guard")));
 | |
| 
 | |
|   EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|             "#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FOO_H\n"
 | |
|             "\n"
 | |
|             "\n"
 | |
|             "#endif\n",
 | |
|             runHeaderGuardCheck(
 | |
|                 "", "\\\\?\\C:\\llvm-project\\clang-tools-extra\\clangd\\foo.h",
 | |
|                 StringRef("header is missing header guard")));
 | |
| #endif
 | |
| }
 | |
| 
 | |
| TEST(IncludeOrderCheck, GTestHeaders) {
 | |
|   EXPECT_EQ(
 | |
|       R"cpp(
 | |
|   #include "foo.h"
 | |
|   #include "llvm/foo.h"
 | |
|   #include "gtest/foo.h"
 | |
|   #include <algorithm>)cpp",
 | |
|       runIncludeOrderCheck(
 | |
|           R"cpp(
 | |
|   #include "foo.h"
 | |
|   #include "llvm/foo.h"
 | |
|   #include <algorithm>
 | |
|   #include "gtest/foo.h")cpp",
 | |
|           "foo.cc", StringRef("#includes are not sorted properly"),
 | |
|           {"foo.h", "algorithm", "gtest/foo.h", "llvm/foo.h"}));
 | |
| }
 | |
| 
 | |
| } // namespace test
 | |
| } // namespace tidy
 | |
| } // namespace clang
 |