Skip to content

[clang-tidy] Add avoid-pragma-once. #140388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

dl8sd11
Copy link
Contributor

@dl8sd11 dl8sd11 commented May 17, 2025

Copy link

github-actions bot commented May 17, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@dl8sd11 dl8sd11 marked this pull request as ready for review May 18, 2025 06:01
@llvmbot
Copy link
Member

llvmbot commented May 18, 2025

@llvm/pr-subscribers-clang-tidy

Author: Tommy Chen (dl8sd11)

Changes

#139618


Full diff: https://github.com/llvm/llvm-project/pull/140388.diff

9 Files Affected:

  • (added) clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.cpp (+49)
  • (added) clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.h (+34)
  • (modified) clang-tools-extra/clang-tidy/portability/CMakeLists.txt (+1)
  • (modified) clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp (+3)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+5)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/docs/clang-tidy/checks/portability/avoid-pragma-once.rst (+12)
  • (added) clang-tools-extra/test/clang-tidy/checkers/portability/Inputs/avoid-pragma-once/lib.h (+1)
  • (added) clang-tools-extra/test/clang-tidy/checkers/portability/avoid-pragma-once.cpp (+5)
diff --git a/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.cpp b/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.cpp
new file mode 100644
index 0000000000000..9a835efd4b9e7
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.cpp
@@ -0,0 +1,49 @@
+//===--- AvoidPragmaOnceCheck.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 "AvoidPragmaOnceCheck.h"
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang::tidy::portability {
+
+class PragmaOnceCallbacks : public PPCallbacks {
+public:
+  PragmaOnceCallbacks(AvoidPragmaOnceCheck *Check, const SourceManager &SM)
+      : Check(Check), SM(SM) {}
+  void PragmaDirective(SourceLocation Loc,
+                       PragmaIntroducerKind Introducer) override {
+    auto Str = llvm::StringRef(SM.getCharacterData(Loc));
+    if (!Str.consume_front("#")) {
+      return;
+    }
+    Str = Str.trim();
+    if (!Str.consume_front("pragma")) {
+      return;
+    }
+    Str = Str.trim();
+    if (Str.starts_with("once")) {
+      Check->diag(Loc, "Avoid pragma once.");
+    }
+  }
+
+private:
+  AvoidPragmaOnceCheck *Check;
+  const SourceManager &SM;
+};
+
+void AvoidPragmaOnceCheck::registerPPCallbacks(const SourceManager &SM,
+                                               Preprocessor *PP,
+                                               Preprocessor *ModuleExpanderPP) {
+  PP->addPPCallbacks(std::make_unique<PragmaOnceCallbacks>(this, SM));
+}
+
+} // namespace clang::tidy::portability
diff --git a/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.h b/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.h
new file mode 100644
index 0000000000000..7208872d416b4
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.h
@@ -0,0 +1,34 @@
+//===--- AvoidPragmaOnceCheck.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_PORTABILITY_AVOIDPRAGMAONCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPRAGMAONCECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::portability {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/portability/avoid-pragma-once.html
+class AvoidPragmaOnceCheck : public ClangTidyCheck {
+public:
+  AvoidPragmaOnceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+};
+
+} // namespace clang::tidy::portability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPRAGMAONCECHECK_H
diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
index 5a38722a61481..73d74a550afc0 100644
--- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_clang_library(clangTidyPortabilityModule STATIC
+  AvoidPragmaOnceCheck.cpp
   PortabilityTidyModule.cpp
   RestrictSystemIncludesCheck.cpp
   SIMDIntrinsicsCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
index 316b98b46cf3f..a15cb36dfdaff 100644
--- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
@@ -9,6 +9,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "AvoidPragmaOnceCheck.h"
 #include "RestrictSystemIncludesCheck.h"
 #include "SIMDIntrinsicsCheck.h"
 #include "StdAllocatorConstCheck.h"
@@ -20,6 +21,8 @@ namespace portability {
 class PortabilityModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<AvoidPragmaOnceCheck>(
+        "portability-avoid-pragma-once");
     CheckFactories.registerCheck<RestrictSystemIncludesCheck>(
         "portability-restrict-system-includes");
     CheckFactories.registerCheck<SIMDIntrinsicsCheck>(
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9b29ab12e1bfa..2b8d37054716b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -142,6 +142,11 @@ New checks
   Finds potentially erroneous calls to ``reset`` method on smart pointers when
   the pointee type also has a ``reset`` method.
 
+- New :doc:`portability-avoid-pragma-once
+  <clang-tidy/checks/portability/avoid-pragma-once>` check.
+
+  A check that catches pragma once.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 1ec476eef3420..607c526621cdf 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -351,6 +351,7 @@ Clang-Tidy Checks
    :doc:`performance-type-promotion-in-math-fn <performance/type-promotion-in-math-fn>`, "Yes"
    :doc:`performance-unnecessary-copy-initialization <performance/unnecessary-copy-initialization>`, "Yes"
    :doc:`performance-unnecessary-value-param <performance/unnecessary-value-param>`, "Yes"
+   :doc:`portability-avoid-pragma-once <portability/avoid-pragma-once>`, "Yes"
    :doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
    :doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
    :doc:`portability-std-allocator-const <portability/std-allocator-const>`,
diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-pragma-once.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-pragma-once.rst
new file mode 100644
index 0000000000000..721511bf395c1
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-pragma-once.rst
@@ -0,0 +1,12 @@
+.. title:: clang-tidy - portability-avoid-pragma-once
+
+portability-avoid-pragma-once
+=============================
+
+This check catches pragma once usage.
+
+For example:
+
+```
+#pragma once // Bad: Avoid pragma once.
+```
diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/Inputs/avoid-pragma-once/lib.h b/clang-tools-extra/test/clang-tidy/checkers/portability/Inputs/avoid-pragma-once/lib.h
new file mode 100644
index 0000000000000..6f70f09beec22
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/portability/Inputs/avoid-pragma-once/lib.h
@@ -0,0 +1 @@
+#pragma once
diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-pragma-once.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-pragma-once.cpp
new file mode 100644
index 0000000000000..e84ad10cb1d93
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-pragma-once.cpp
@@ -0,0 +1,5 @@
+// RUN: %check_clang_tidy %s portability-avoid-pragma-once %t \
+// RUN:   -- --header-filter='.*' --  -I%S/Inputs/avoid-pragma-once
+
+#include "lib.h"
+// CHECK-MESSAGES: lib.h:1:1: warning: Avoid pragma once.
\ No newline at end of file

@llvmbot
Copy link
Member

llvmbot commented May 18, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Tommy Chen (dl8sd11)

Changes

#139618


Full diff: https://github.com/llvm/llvm-project/pull/140388.diff

9 Files Affected:

  • (added) clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.cpp (+49)
  • (added) clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.h (+34)
  • (modified) clang-tools-extra/clang-tidy/portability/CMakeLists.txt (+1)
  • (modified) clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp (+3)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+5)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/docs/clang-tidy/checks/portability/avoid-pragma-once.rst (+12)
  • (added) clang-tools-extra/test/clang-tidy/checkers/portability/Inputs/avoid-pragma-once/lib.h (+1)
  • (added) clang-tools-extra/test/clang-tidy/checkers/portability/avoid-pragma-once.cpp (+5)
diff --git a/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.cpp b/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.cpp
new file mode 100644
index 0000000000000..9a835efd4b9e7
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.cpp
@@ -0,0 +1,49 @@
+//===--- AvoidPragmaOnceCheck.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 "AvoidPragmaOnceCheck.h"
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang::tidy::portability {
+
+class PragmaOnceCallbacks : public PPCallbacks {
+public:
+  PragmaOnceCallbacks(AvoidPragmaOnceCheck *Check, const SourceManager &SM)
+      : Check(Check), SM(SM) {}
+  void PragmaDirective(SourceLocation Loc,
+                       PragmaIntroducerKind Introducer) override {
+    auto Str = llvm::StringRef(SM.getCharacterData(Loc));
+    if (!Str.consume_front("#")) {
+      return;
+    }
+    Str = Str.trim();
+    if (!Str.consume_front("pragma")) {
+      return;
+    }
+    Str = Str.trim();
+    if (Str.starts_with("once")) {
+      Check->diag(Loc, "Avoid pragma once.");
+    }
+  }
+
+private:
+  AvoidPragmaOnceCheck *Check;
+  const SourceManager &SM;
+};
+
+void AvoidPragmaOnceCheck::registerPPCallbacks(const SourceManager &SM,
+                                               Preprocessor *PP,
+                                               Preprocessor *ModuleExpanderPP) {
+  PP->addPPCallbacks(std::make_unique<PragmaOnceCallbacks>(this, SM));
+}
+
+} // namespace clang::tidy::portability
diff --git a/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.h b/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.h
new file mode 100644
index 0000000000000..7208872d416b4
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/portability/AvoidPragmaOnceCheck.h
@@ -0,0 +1,34 @@
+//===--- AvoidPragmaOnceCheck.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_PORTABILITY_AVOIDPRAGMAONCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPRAGMAONCECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::portability {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/portability/avoid-pragma-once.html
+class AvoidPragmaOnceCheck : public ClangTidyCheck {
+public:
+  AvoidPragmaOnceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+};
+
+} // namespace clang::tidy::portability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPRAGMAONCECHECK_H
diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
index 5a38722a61481..73d74a550afc0 100644
--- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_clang_library(clangTidyPortabilityModule STATIC
+  AvoidPragmaOnceCheck.cpp
   PortabilityTidyModule.cpp
   RestrictSystemIncludesCheck.cpp
   SIMDIntrinsicsCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
index 316b98b46cf3f..a15cb36dfdaff 100644
--- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
@@ -9,6 +9,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "AvoidPragmaOnceCheck.h"
 #include "RestrictSystemIncludesCheck.h"
 #include "SIMDIntrinsicsCheck.h"
 #include "StdAllocatorConstCheck.h"
@@ -20,6 +21,8 @@ namespace portability {
 class PortabilityModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<AvoidPragmaOnceCheck>(
+        "portability-avoid-pragma-once");
     CheckFactories.registerCheck<RestrictSystemIncludesCheck>(
         "portability-restrict-system-includes");
     CheckFactories.registerCheck<SIMDIntrinsicsCheck>(
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 9b29ab12e1bfa..2b8d37054716b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -142,6 +142,11 @@ New checks
   Finds potentially erroneous calls to ``reset`` method on smart pointers when
   the pointee type also has a ``reset`` method.
 
+- New :doc:`portability-avoid-pragma-once
+  <clang-tidy/checks/portability/avoid-pragma-once>` check.
+
+  A check that catches pragma once.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 1ec476eef3420..607c526621cdf 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -351,6 +351,7 @@ Clang-Tidy Checks
    :doc:`performance-type-promotion-in-math-fn <performance/type-promotion-in-math-fn>`, "Yes"
    :doc:`performance-unnecessary-copy-initialization <performance/unnecessary-copy-initialization>`, "Yes"
    :doc:`performance-unnecessary-value-param <performance/unnecessary-value-param>`, "Yes"
+   :doc:`portability-avoid-pragma-once <portability/avoid-pragma-once>`, "Yes"
    :doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
    :doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
    :doc:`portability-std-allocator-const <portability/std-allocator-const>`,
diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-pragma-once.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-pragma-once.rst
new file mode 100644
index 0000000000000..721511bf395c1
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-pragma-once.rst
@@ -0,0 +1,12 @@
+.. title:: clang-tidy - portability-avoid-pragma-once
+
+portability-avoid-pragma-once
+=============================
+
+This check catches pragma once usage.
+
+For example:
+
+```
+#pragma once // Bad: Avoid pragma once.
+```
diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/Inputs/avoid-pragma-once/lib.h b/clang-tools-extra/test/clang-tidy/checkers/portability/Inputs/avoid-pragma-once/lib.h
new file mode 100644
index 0000000000000..6f70f09beec22
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/portability/Inputs/avoid-pragma-once/lib.h
@@ -0,0 +1 @@
+#pragma once
diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-pragma-once.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-pragma-once.cpp
new file mode 100644
index 0000000000000..e84ad10cb1d93
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-pragma-once.cpp
@@ -0,0 +1,5 @@
+// RUN: %check_clang_tidy %s portability-avoid-pragma-once %t \
+// RUN:   -- --header-filter='.*' --  -I%S/Inputs/avoid-pragma-once
+
+#include "lib.h"
+// CHECK-MESSAGES: lib.h:1:1: warning: Avoid pragma once.
\ No newline at end of file

Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good check!
Mostly docs/phrasing comments from me.

@dl8sd11
Copy link
Contributor Author

dl8sd11 commented May 18, 2025

Fixed!

First time adding a clang-tidy check! Apologies for missing the conventions, and huge thanks for your time reviewing.

Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more nit changes.
If you want to contribute further, reading https://clang.llvm.org/extra/clang-tidy/Contributing.html may help you.
Particularly "Making your check robust" and "Documenting your check". Although, most of it already covered in comments.

Copy link
Member

@PiotrZSL PiotrZSL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation may require some improvements, but overall looks fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants