#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" #include using namespace clang::tidy::llvm_check; namespace clang { namespace tidy { namespace test { template static std::string runCheck(StringRef Code, const Twine &Filename, std::optional ExpectedWarning, std::map PathsToContent = std::map()) { std::vector Errors; std::string Result = test::runCheckOnCode( 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, std::optional ExpectedWarning) { return runCheck(Code, Filename, std::move(ExpectedWarning)); } static std::string runIncludeOrderCheck(StringRef Code, const Twine &Filename, std::optional ExpectedWarning, llvm::ArrayRef Includes) { std::map PathsToContent; for (auto Include : Includes) PathsToContent.emplace(Include, ""); return runCheck(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, std::optional ExpectedWarning) { return runCheck(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", std::nullopt)); 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", std::nullopt)); 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", std::nullopt)); 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", std::nullopt)); 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", std::nullopt)); 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 )cpp", runIncludeOrderCheck( R"cpp( #include "foo.h" #include "llvm/foo.h" #include #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