Skip to content

Commit 4cb25e2

Browse files
authored
[clang-tidy] Add avoid-pragma-once. (#140388)
#139618
1 parent 86eb419 commit 4cb25e2

File tree

11 files changed

+147
-0
lines changed

11 files changed

+147
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===--- AvoidPragmaOnceCheck.cpp - clang-tidy ----------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "AvoidPragmaOnceCheck.h"
10+
11+
#include "clang/Basic/SourceManager.h"
12+
#include "clang/Lex/PPCallbacks.h"
13+
#include "clang/Lex/Preprocessor.h"
14+
#include "llvm/ADT/StringRef.h"
15+
16+
namespace clang::tidy::portability {
17+
18+
class PragmaOnceCallbacks : public PPCallbacks {
19+
public:
20+
PragmaOnceCallbacks(AvoidPragmaOnceCheck *Check, const SourceManager &SM)
21+
: Check(Check), SM(SM) {}
22+
void PragmaDirective(SourceLocation Loc,
23+
PragmaIntroducerKind Introducer) override {
24+
auto Str = llvm::StringRef(SM.getCharacterData(Loc));
25+
if (!Str.consume_front("#"))
26+
return;
27+
Str = Str.trim();
28+
if (!Str.consume_front("pragma"))
29+
return;
30+
Str = Str.trim();
31+
if (Str.starts_with("once"))
32+
Check->diag(Loc,
33+
"avoid 'pragma once' directive; use include guards instead");
34+
}
35+
36+
private:
37+
AvoidPragmaOnceCheck *Check;
38+
const SourceManager &SM;
39+
};
40+
41+
void AvoidPragmaOnceCheck::registerPPCallbacks(const SourceManager &SM,
42+
Preprocessor *PP,
43+
Preprocessor *ModuleExpanderPP) {
44+
PP->addPPCallbacks(std::make_unique<PragmaOnceCallbacks>(this, SM));
45+
}
46+
47+
} // namespace clang::tidy::portability
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===--- AvoidPragmaOnceCheck.h - clang-tidy --------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPRAGMAONCECHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPRAGMAONCECHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::portability {
15+
16+
/// Finds uses of ``#pragma once`` and suggests replacing them with standard
17+
/// include guards (``#ifndef``/``#define``/``#endif``) for improved
18+
/// portability.
19+
///
20+
/// For the user-facing documentation see:
21+
/// http://clang.llvm.org/extra/clang-tidy/checks/portability/avoid-pragma-once.html
22+
class AvoidPragmaOnceCheck : public ClangTidyCheck {
23+
public:
24+
AvoidPragmaOnceCheck(StringRef Name, ClangTidyContext *Context)
25+
: ClangTidyCheck(Name, Context) {}
26+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
27+
return LangOpts.CPlusPlus || LangOpts.C99;
28+
}
29+
30+
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
31+
Preprocessor *ModuleExpanderPP) override;
32+
};
33+
34+
} // namespace clang::tidy::portability
35+
36+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPRAGMAONCECHECK_H

clang-tools-extra/clang-tidy/portability/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
55
)
66

77
add_clang_library(clangTidyPortabilityModule STATIC
8+
AvoidPragmaOnceCheck.cpp
89
PortabilityTidyModule.cpp
910
RestrictSystemIncludesCheck.cpp
1011
SIMDIntrinsicsCheck.cpp

clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "../ClangTidy.h"
1010
#include "../ClangTidyModule.h"
1111
#include "../ClangTidyModuleRegistry.h"
12+
#include "AvoidPragmaOnceCheck.h"
1213
#include "RestrictSystemIncludesCheck.h"
1314
#include "SIMDIntrinsicsCheck.h"
1415
#include "StdAllocatorConstCheck.h"
@@ -20,6 +21,8 @@ namespace portability {
2021
class PortabilityModule : public ClangTidyModule {
2122
public:
2223
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
24+
CheckFactories.registerCheck<AvoidPragmaOnceCheck>(
25+
"portability-avoid-pragma-once");
2326
CheckFactories.registerCheck<RestrictSystemIncludesCheck>(
2427
"portability-restrict-system-includes");
2528
CheckFactories.registerCheck<SIMDIntrinsicsCheck>(

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ New checks
136136
Finds unintended character output from ``unsigned char`` and ``signed char``
137137
to an ``ostream``.
138138

139+
- New :doc:`portability-avoid-pragma-once
140+
<clang-tidy/checks/portability/avoid-pragma-once>` check.
141+
142+
Finds uses of ``#pragma once`` and suggests replacing them with standard
143+
include guards (``#ifndef``/``#define``/``#endif``) for improved portability.
144+
139145
- New :doc:`readability-ambiguous-smartptr-reset-call
140146
<clang-tidy/checks/readability/ambiguous-smartptr-reset-call>` check.
141147

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ Clang-Tidy Checks
351351
:doc:`performance-type-promotion-in-math-fn <performance/type-promotion-in-math-fn>`, "Yes"
352352
:doc:`performance-unnecessary-copy-initialization <performance/unnecessary-copy-initialization>`, "Yes"
353353
:doc:`performance-unnecessary-value-param <performance/unnecessary-value-param>`, "Yes"
354+
:doc:`portability-avoid-pragma-once <portability/avoid-pragma-once>`,
354355
:doc:`portability-restrict-system-includes <portability/restrict-system-includes>`, "Yes"
355356
:doc:`portability-simd-intrinsics <portability/simd-intrinsics>`,
356357
:doc:`portability-std-allocator-const <portability/std-allocator-const>`,
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.. title:: clang-tidy - portability-avoid-pragma-once
2+
3+
portability-avoid-pragma-once
4+
=============================
5+
6+
Finds uses of ``#pragma once`` and suggests replacing them with standard
7+
include guards (``#ifndef``/``#define``/``#endif``) for improved portability.
8+
9+
``#pragma once`` is a non-standard extension, despite being widely supported
10+
by modern compilers. Relying on it can lead to portability issues in
11+
some environments.
12+
13+
Some older or specialized C/C++ compilers, particularly in embedded systems,
14+
may not fully support ``#pragma once``.
15+
16+
It can also fail in certain file system configurations, like network drives
17+
or complex symbolic links, potentially leading to compilation issues.
18+
19+
Consider the following header file:
20+
21+
.. code:: c++
22+
23+
// my_header.h
24+
#pragma once // warning: avoid 'pragma once' directive; use include guards instead
25+
26+
27+
The warning suggests using include guards:
28+
29+
.. code:: c++
30+
31+
// my_header.h
32+
#ifndef PATH_TO_MY_HEADER_H // Good: use include guards.
33+
#define PATH_TO_MY_HEADER_H
34+
35+
#endif // PATH_TO_MY_HEADER_H
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#pragma once
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# pragma once
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# pragma once
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %check_clang_tidy %s portability-avoid-pragma-once %t \
2+
// RUN: -- --header-filter='.*' -- -I%S/Inputs/avoid-pragma-once
3+
4+
// #pragma once
5+
#include "lib0.h"
6+
// CHECK-MESSAGES: lib0.h:1:1: warning: avoid 'pragma once' directive; use include guards instead
7+
8+
9+
// # pragma once
10+
#include "lib1.h"
11+
// CHECK-MESSAGES: lib1.h:1:1: warning: avoid 'pragma once' directive; use include guards instead
12+
13+
// # pragma once
14+
#include "lib2.h"
15+
// CHECK-MESSAGES: lib2.h:1:1: warning: avoid 'pragma once' directive; use include guards instead

0 commit comments

Comments
 (0)