KyleFromKitware created this revision.
KyleFromKitware added a reviewer: clang-tools-extra.
KyleFromKitware created this object with edit policy "Only User: 
KyleFromKitware (Kyle Edwards)".
KyleFromKitware added a project: clang-tools-extra.
Herald added subscribers: carlosgalvezp, xazax.hun.
Herald added a reviewer: njames93.
Herald added a project: All.
KyleFromKitware requested review of this revision.
Herald added a subscriber: cfe-commits.

Co-Authored-by: Igor-Mikhail-Valentin Glebov <igle...@albany.edu>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D142123

Files:
  clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
  clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
  clang-tools-extra/clang-tidy/modernize/UsePragmaOnceCheck.cpp
  clang-tools-extra/clang-tidy/modernize/UsePragmaOnceCheck.h
  clang-tools-extra/unittests/clang-tidy/ModernizeModuleTest.cpp

Index: clang-tools-extra/unittests/clang-tidy/ModernizeModuleTest.cpp
===================================================================
--- clang-tools-extra/unittests/clang-tidy/ModernizeModuleTest.cpp
+++ clang-tools-extra/unittests/clang-tidy/ModernizeModuleTest.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 #include "ClangTidyTest.h"
 #include "modernize/IntegralLiteralExpressionMatcher.h"
+#include "modernize/UsePragmaOnceCheck.h"
 #include "clang/Lex/Lexer.h"
 #include "gtest/gtest.h"
 
@@ -272,6 +273,62 @@
 INSTANTIATE_TEST_SUITE_P(IntegralLiteralExpressionMatcherTests, SizeTest,
                          ::testing::ValuesIn(SizeParams));
 
+namespace {
+std::string runPragmaOnceCheck(StringRef Code, const Twine &Filename,
+                               std::optional<StringRef> ExpectedWarning,
+                               std::map<StringRef, StringRef> PathsToContent =
+                                   std::map<StringRef, StringRef>()) {
+  std::vector<ClangTidyError> Errors;
+  std::string Result = test::runCheckOnCode<modernize::UsePragmaOnceCheck>(
+      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;
+}
+} // namespace
+
+TEST(UsePragmaOnceCheckTest, AddPragmaOnce) {
+  EXPECT_EQ("#pragma once\n"
+            "\n"
+            "void headerGuard();\n"
+            "\n",
+            runPragmaOnceCheck("#ifndef HEADER_GUARD_H\n"
+                               "#define HEADER_GUARD_H\n"
+                               "\n"
+                               "void headerGuard();\n"
+                               "\n"
+                               "#endif // HEADER_GUARD_H\n",
+                               "header-guard.h",
+                               StringRef("use #pragma once")));
+  EXPECT_EQ("#pragma once\n"
+            "\n"
+            "void pragmaOnce();\n",
+            runPragmaOnceCheck("#pragma once\n"
+                               "\n"
+                               "void pragmaOnce();\n",
+                               "pragma-once.h", std::nullopt));
+  EXPECT_EQ("#pragma once\n"
+            "\n"
+            "void both();\n"
+            "\n",
+            runPragmaOnceCheck("#ifndef BOTH_H\n"
+                               "#define BOTH_H\n"
+                               "#pragma once\n"
+                               "\n"
+                               "void both();\n"
+                               "\n"
+                               "#endif // BOTH_H\n",
+                               "both.h", StringRef("use #pragma once")));
+  EXPECT_EQ("#pragma once\n"
+            "void neither();\n",
+            runPragmaOnceCheck("void neither();\n", "neither.h",
+                               StringRef("use #pragma once")));
+}
+
 } // namespace test
 } // namespace tidy
 } // namespace clang
Index: clang-tools-extra/clang-tidy/modernize/UsePragmaOnceCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/modernize/UsePragmaOnceCheck.h
@@ -0,0 +1,47 @@
+//===--- UsePragmaOnceCheck.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEPRAGMAONCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEPRAGMAONCECHECK_H
+
+#include "../utils/HeaderGuardBase.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Check that suggests the use of #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 utils::HeaderGuardBase {
+public:
+  UsePragmaOnceCheck(StringRef Name, ClangTidyContext *Context)
+      : HeaderGuardBase(Name, Context) {}
+
+  void onHeaderGuard(Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+                     SourceLocation IfndefHash, SourceLocation Ifndef,
+                     SourceLocation IfndefToken, SourceLocation DefineHash,
+                     const Token &Define, SourceLocation EndIfHash,
+                     SourceLocation EndIf) override;
+
+  void onGuardlessHeader(
+      Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+      SourceLocation StartLoc,
+      const std::vector<std::tuple<SourceLocation, Token, const MacroInfo *>>
+          &Macros) override;
+};
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEPRAGMAONCECHECK_H
Index: clang-tools-extra/clang-tidy/modernize/UsePragmaOnceCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/modernize/UsePragmaOnceCheck.cpp
@@ -0,0 +1,72 @@
+//===--- UsePragmaOnceCheck.cpp - 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "UsePragmaOnceCheck.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang::tidy::modernize {
+namespace {
+CharSourceRange goToLineEnd(Preprocessor *PP, SourceLocation StartLoc,
+                            SourceLocation EndLoc) {
+  SourceManager &SM = PP->getSourceManager();
+  FileID FID = SM.getFileID(EndLoc);
+  StringRef BufData = SM.getBufferData(FID);
+  const char *EndData = BufData.begin() + SM.getFileOffset(EndLoc);
+  Lexer Lex(EndLoc, PP->getLangOpts(), BufData.begin(), EndData, BufData.end());
+  // FIXME: this is a bit hacky to get ReadToEndOfLine work.
+  Lex.setParsingPreprocessorDirective(true);
+  Lex.ReadToEndOfLine();
+  return CharSourceRange::getCharRange(
+      StartLoc, SM.getLocForStartOfFile(FID).getLocWithOffset(
+                    Lex.getCurrentBufferOffset()));
+}
+} // namespace
+
+void UsePragmaOnceCheck::onHeaderGuard(
+    Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+    SourceLocation IfndefHash, SourceLocation Ifndef,
+    SourceLocation IfndefToken, SourceLocation DefineHash, const Token &Define,
+    SourceLocation EndIfHash, SourceLocation EndIf) {
+  std::vector<FixItHint> FixIts;
+
+  HeaderSearch &HeaderInfo = PP->getHeaderSearchInfo();
+
+  HeaderFileInfo &Info = HeaderInfo.getFileInfo(FE);
+
+  CharSourceRange IfndefSrcRange = goToLineEnd(PP, IfndefHash, IfndefToken);
+  CharSourceRange DefineSrcRange =
+      goToLineEnd(PP, DefineHash, Define.getLocation());
+  CharSourceRange EndifSrcRange = goToLineEnd(PP, EndIfHash, EndIf);
+
+  if (Info.isPragmaOnce)
+    FixIts.push_back(FixItHint::CreateRemoval(IfndefSrcRange));
+  else
+    FixIts.push_back(
+        FixItHint::CreateReplacement(IfndefSrcRange, "#pragma once\n"));
+
+  FixIts.push_back(FixItHint::CreateRemoval(DefineSrcRange));
+  FixIts.push_back(FixItHint::CreateRemoval(EndifSrcRange));
+
+  diag(IfndefSrcRange.getBegin(), "use #pragma once") << FixIts;
+}
+
+void UsePragmaOnceCheck::onGuardlessHeader(
+    Preprocessor *PP, StringRef FileName, const FileEntry *FE,
+    SourceLocation StartLoc,
+    const std::vector<std::tuple<SourceLocation, Token, const MacroInfo *>>
+        &Macros) {
+  HeaderSearch &HeaderInfo = PP->getHeaderSearchInfo();
+
+  HeaderFileInfo &Info = HeaderInfo.getFileInfo(FE);
+  if (Info.isPragmaOnce)
+    return;
+
+  diag(StartLoc, "use #pragma once")
+      << FixItHint::CreateInsertion(StartLoc, "#pragma once\n");
+}
+} // namespace clang::tidy::modernize
Index: clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -37,6 +37,7 @@
 #include "UseNoexceptCheck.h"
 #include "UseNullptrCheck.h"
 #include "UseOverrideCheck.h"
+#include "UsePragmaOnceCheck.h"
 #include "UseTrailingReturnTypeCheck.h"
 #include "UseTransparentFunctorsCheck.h"
 #include "UseUncaughtExceptionsCheck.h"
@@ -92,6 +93,8 @@
     CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
     CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
     CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
+    CheckFactories.registerCheck<UsePragmaOnceCheck>(
+        "modernize-use-pragma-once");
     CheckFactories.registerCheck<UseTrailingReturnTypeCheck>(
         "modernize-use-trailing-return-type");
     CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
Index: clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -36,6 +36,7 @@
   UseNoexceptCheck.cpp
   UseNullptrCheck.cpp
   UseOverrideCheck.cpp
+  UsePragmaOnceCheck.cpp
   UseTrailingReturnTypeCheck.cpp
   UseTransparentFunctorsCheck.cpp
   UseUncaughtExceptionsCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to