Skip to content

Commit 59b39c0

Browse files
authored
[clang-tidy] Add new check: readability-use-concise-preprocessor-directives (#146830)
Closes #132561. This is a check that rewrites `#if`s and `#elif`s like so: ```cpp #if defined(MEOW) // -> #ifdef MEOW #if !defined(MEOW) // -> #ifndef MEOW ``` And, since C23 and C++23: ```cpp #elif defined(MEOW) // -> #elifdef MEOW #elif !defined(MEOW) // -> #elifndef MEOW ```
1 parent 1db33f1 commit 59b39c0

File tree

8 files changed

+330
-0
lines changed

8 files changed

+330
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
5858
UniqueptrDeleteReleaseCheck.cpp
5959
UppercaseLiteralSuffixCheck.cpp
6060
UseAnyOfAllOfCheck.cpp
61+
UseConcisePreprocessorDirectivesCheck.cpp
6162
UseStdMinMaxCheck.cpp
6263

6364
LINK_LIBS

clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "UniqueptrDeleteReleaseCheck.h"
6262
#include "UppercaseLiteralSuffixCheck.h"
6363
#include "UseAnyOfAllOfCheck.h"
64+
#include "UseConcisePreprocessorDirectivesCheck.h"
6465
#include "UseStdMinMaxCheck.h"
6566

6667
namespace clang::tidy {
@@ -173,6 +174,8 @@ class ReadabilityModule : public ClangTidyModule {
173174
"readability-uppercase-literal-suffix");
174175
CheckFactories.registerCheck<UseAnyOfAllOfCheck>(
175176
"readability-use-anyofallof");
177+
CheckFactories.registerCheck<UseConcisePreprocessorDirectivesCheck>(
178+
"readability-use-concise-preprocessor-directives");
176179
CheckFactories.registerCheck<UseStdMinMaxCheck>(
177180
"readability-use-std-min-max");
178181
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//===--- UseConcisePreprocessorDirectivesCheck.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 "UseConcisePreprocessorDirectivesCheck.h"
10+
#include "clang/Basic/TokenKinds.h"
11+
#include "clang/Lex/Lexer.h"
12+
#include "clang/Lex/PPCallbacks.h"
13+
#include "clang/Lex/Preprocessor.h"
14+
15+
#include <array>
16+
17+
namespace clang::tidy::readability {
18+
19+
namespace {
20+
21+
class IfPreprocessorCallbacks final : public PPCallbacks {
22+
public:
23+
IfPreprocessorCallbacks(ClangTidyCheck &Check, const Preprocessor &PP)
24+
: Check(Check), PP(PP) {}
25+
26+
void If(SourceLocation Loc, SourceRange ConditionRange,
27+
ConditionValueKind) override {
28+
impl(Loc, ConditionRange, {"ifdef", "ifndef"});
29+
}
30+
31+
void Elif(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind,
32+
SourceLocation) override {
33+
if (PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus23)
34+
impl(Loc, ConditionRange, {"elifdef", "elifndef"});
35+
}
36+
37+
private:
38+
void impl(SourceLocation DirectiveLoc, SourceRange ConditionRange,
39+
const std::array<llvm::StringLiteral, 2> &Replacements) {
40+
// Lexer requires its input range to be null-terminated.
41+
SmallString<128> Condition =
42+
Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange),
43+
PP.getSourceManager(), PP.getLangOpts());
44+
Condition.push_back('\0');
45+
Lexer Lex(DirectiveLoc, PP.getLangOpts(), Condition.data(),
46+
Condition.data(), Condition.data() + Condition.size() - 1);
47+
Token Tok;
48+
bool Inverted = false; // The inverted form of #*def is #*ndef.
49+
std::size_t ParensNestingDepth = 0;
50+
for (;;) {
51+
if (Lex.LexFromRawLexer(Tok))
52+
return;
53+
54+
if (Tok.is(tok::TokenKind::exclaim) ||
55+
(PP.getLangOpts().CPlusPlus &&
56+
Tok.is(tok::TokenKind::raw_identifier) &&
57+
Tok.getRawIdentifier() == "not"))
58+
Inverted = !Inverted;
59+
else if (Tok.is(tok::TokenKind::l_paren))
60+
++ParensNestingDepth;
61+
else
62+
break;
63+
}
64+
65+
if (Tok.isNot(tok::TokenKind::raw_identifier) ||
66+
Tok.getRawIdentifier() != "defined")
67+
return;
68+
69+
bool NoMoreTokens = Lex.LexFromRawLexer(Tok);
70+
if (Tok.is(tok::TokenKind::l_paren)) {
71+
if (NoMoreTokens)
72+
return;
73+
++ParensNestingDepth;
74+
NoMoreTokens = Lex.LexFromRawLexer(Tok);
75+
}
76+
77+
if (Tok.isNot(tok::TokenKind::raw_identifier))
78+
return;
79+
const StringRef Macro = Tok.getRawIdentifier();
80+
81+
while (!NoMoreTokens) {
82+
NoMoreTokens = Lex.LexFromRawLexer(Tok);
83+
if (Tok.isNot(tok::TokenKind::r_paren))
84+
return;
85+
--ParensNestingDepth;
86+
}
87+
88+
if (ParensNestingDepth != 0)
89+
return;
90+
91+
Check.diag(
92+
DirectiveLoc,
93+
"preprocessor condition can be written more concisely using '#%0'")
94+
<< FixItHint::CreateReplacement(DirectiveLoc, Replacements[Inverted])
95+
<< FixItHint::CreateReplacement(ConditionRange, Macro)
96+
<< Replacements[Inverted];
97+
}
98+
99+
ClangTidyCheck &Check;
100+
const Preprocessor &PP;
101+
};
102+
103+
} // namespace
104+
105+
void UseConcisePreprocessorDirectivesCheck::registerPPCallbacks(
106+
const SourceManager &, Preprocessor *PP, Preprocessor *) {
107+
PP->addPPCallbacks(std::make_unique<IfPreprocessorCallbacks>(*this, *PP));
108+
}
109+
110+
} // namespace clang::tidy::readability
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- UseConcisePreprocessorDirectivesCheck.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_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::readability {
15+
16+
/// Finds uses of ``#if`` that can be simplified to ``#ifdef`` or ``#ifndef``
17+
/// and, since C23 and C++23, uses of ``#elif`` that can be simplified to
18+
/// ``#elifdef`` or ``#elifndef``.
19+
///
20+
/// User-facing documentation:
21+
/// https://clang.llvm.org/extra/clang-tidy/checks/readability/use-concise-preprocessor-directives.html
22+
class UseConcisePreprocessorDirectivesCheck : public ClangTidyCheck {
23+
public:
24+
using ClangTidyCheck::ClangTidyCheck;
25+
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
26+
Preprocessor *ModuleExpanderPP) override;
27+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
28+
return true;
29+
}
30+
};
31+
32+
} // namespace clang::tidy::readability
33+
34+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ New checks
166166
Finds potentially erroneous calls to ``reset`` method on smart pointers when
167167
the pointee type also has a ``reset`` method.
168168

169+
- New :doc:`readability-use-concise-preprocessor-directives
170+
<clang-tidy/checks/readability/use-concise-preprocessor-directives>` check.
171+
172+
Finds uses of ``#if`` that can be simplified to ``#ifdef`` or ``#ifndef`` and,
173+
since C23 and C++23, uses of ``#elif`` that can be simplified to ``#elifdef``
174+
or ``#elifndef``.
175+
169176
New check aliases
170177
^^^^^^^^^^^^^^^^^
171178

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ Clang-Tidy Checks
411411
:doc:`readability-uniqueptr-delete-release <readability/uniqueptr-delete-release>`, "Yes"
412412
:doc:`readability-uppercase-literal-suffix <readability/uppercase-literal-suffix>`, "Yes"
413413
:doc:`readability-use-anyofallof <readability/use-anyofallof>`,
414+
:doc:`readability-use-concise-preprocessor-directives <readability/use-concise-preprocessor-directives>`, "Yes"
414415
:doc:`readability-use-std-min-max <readability/use-std-min-max>`, "Yes"
415416
:doc:`zircon-temporary-objects <zircon/temporary-objects>`,
416417

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
.. title:: clang-tidy - readability-use-concise-preprocessor-directives
2+
3+
readability-use-concise-preprocessor-directives
4+
===============================================
5+
6+
Finds uses of ``#if`` that can be simplified to ``#ifdef`` or ``#ifndef`` and,
7+
since C23 and C++23, uses of ``#elif`` that can be simplified to ``#elifdef``
8+
or ``#elifndef``:
9+
10+
.. code-block:: c++
11+
12+
#if defined(MEOW)
13+
#if !defined(MEOW)
14+
15+
// becomes
16+
17+
#ifdef MEOW
18+
#ifndef MEOW
19+
20+
Since C23 and C++23:
21+
22+
.. code-block:: c++
23+
24+
#elif defined(MEOW)
25+
#elif !defined(MEOW)
26+
27+
// becomes
28+
29+
#elifdef MEOW
30+
#elifndef MEOW
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// RUN: %check_clang_tidy -std=c++98,c++11,c++14,c++17,c++20 -check-suffixes=,CXX %s readability-use-concise-preprocessor-directives %t
2+
// RUN: %check_clang_tidy -std=c++23-or-later -check-suffixes=,23,CXX,CXX23 %s readability-use-concise-preprocessor-directives %t
3+
4+
// RUN: %check_clang_tidy -std=c99,c11,c17 %s readability-use-concise-preprocessor-directives %t -- -- -x c
5+
// RUN: %check_clang_tidy -std=c23-or-later -check-suffixes=,23 %s readability-use-concise-preprocessor-directives %t -- -- -x c
6+
7+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifdef' [readability-use-concise-preprocessor-directives]
8+
// CHECK-FIXES: #ifdef FOO
9+
#if defined(FOO)
10+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives]
11+
// CHECK-FIXES-23: #elifdef BAR
12+
#elif defined(BAR)
13+
#endif
14+
15+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifdef' [readability-use-concise-preprocessor-directives]
16+
// CHECK-FIXES: #ifdef FOO
17+
#if defined FOO
18+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives]
19+
// CHECK-FIXES-23: #elifdef BAR
20+
#elif defined BAR
21+
#endif
22+
23+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifdef' [readability-use-concise-preprocessor-directives]
24+
// CHECK-FIXES: #ifdef FOO
25+
#if (defined(FOO))
26+
// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives]
27+
// CHECK-FIXES-23: # elifdef BAR
28+
# elif (defined(BAR))
29+
#endif
30+
31+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifdef' [readability-use-concise-preprocessor-directives]
32+
// CHECK-FIXES: #ifdef FOO
33+
#if (defined FOO)
34+
// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives]
35+
// CHECK-FIXES-23: # elifdef BAR
36+
# elif (defined BAR)
37+
#endif
38+
39+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
40+
// CHECK-FIXES: #ifndef FOO
41+
#if !defined(FOO)
42+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives]
43+
// CHECK-FIXES-23: #elifndef BAR
44+
#elif !defined(BAR)
45+
#endif
46+
47+
#ifdef __cplusplus
48+
// CHECK-MESSAGES-CXX: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
49+
// CHECK-FIXES-CXX: #ifndef FOO
50+
#if not defined(FOO)
51+
// CHECK-MESSAGES-CXX23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives]
52+
// CHECK-FIXES-CXX23: #elifndef BAR
53+
#elif not defined(BAR)
54+
#endif
55+
#endif // __cplusplus
56+
57+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
58+
// CHECK-FIXES: #ifndef FOO
59+
#if !defined FOO
60+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives]
61+
// CHECK-FIXES-23: #elifndef BAR
62+
#elif !defined BAR
63+
#endif
64+
65+
#ifdef __cplusplus
66+
// CHECK-MESSAGES-CXX: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
67+
// CHECK-FIXES-CXX: #ifndef FOO
68+
#if not defined FOO
69+
// CHECK-MESSAGES-CXX23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives]
70+
// CHECK-FIXES-CXX23: #elifndef BAR
71+
#elif not defined BAR
72+
#endif
73+
#endif // __cplusplus
74+
75+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
76+
// CHECK-FIXES: #ifndef FOO
77+
#if (!defined(FOO))
78+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives]
79+
// CHECK-FIXES-23: #elifndef BAR
80+
#elif (!defined(BAR))
81+
#endif
82+
83+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
84+
// CHECK-FIXES: #ifndef FOO
85+
#if (!defined FOO)
86+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives]
87+
// CHECK-FIXES-23: #elifndef BAR
88+
#elif (!defined BAR)
89+
#endif
90+
91+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
92+
// CHECK-FIXES: #ifndef FOO
93+
#if !(defined(FOO))
94+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives]
95+
// CHECK-FIXES-23: #elifndef BAR
96+
#elif !(defined(BAR))
97+
#endif
98+
99+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
100+
// CHECK-FIXES: #ifndef FOO
101+
#if !(defined FOO)
102+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives]
103+
// CHECK-FIXES-23: #elifndef BAR
104+
#elif !(defined BAR)
105+
#endif
106+
107+
// These cases with many parentheses and negations are unrealistic, but
108+
// handling them doesn't really add any complexity to the implementation.
109+
// Test them for good measure.
110+
111+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
112+
// CHECK-FIXES: #ifndef FOO
113+
#if !((!!(defined(FOO))))
114+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives]
115+
// CHECK-FIXES-23: #elifdef BAR
116+
#elif ((!(!(defined(BAR)))))
117+
#endif
118+
119+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
120+
// CHECK-FIXES: #ifndef FOO
121+
#if !((!!(defined FOO)))
122+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives]
123+
// CHECK-FIXES-23: #elifdef BAR
124+
#elif ((!(!(defined BAR))))
125+
#endif
126+
127+
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives]
128+
// CHECK-FIXES: #ifndef FOO
129+
#if !( (!! ( defined FOO )) )
130+
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives]
131+
// CHECK-FIXES-23: #elifdef BAR
132+
#elif ( ( !(!( defined BAR) ) ))
133+
#endif
134+
135+
#if FOO
136+
#elif BAR
137+
#endif
138+
139+
#if defined(FOO) && defined(BAR)
140+
#elif defined(FOO) && defined(BAR)
141+
#endif
142+
143+
#if defined FOO && BAR
144+
#endif

0 commit comments

Comments
 (0)