Skip to content

Commit 9eb5843

Browse files
committed
[clang-tidy] Add new check: modernize-use-concise-preprocessor-directives
Rewrites preprocessor conditions like `#if defined(MEOW)` as `#ifdef MEOW` and `#elif !defined(MEOW)` as `#elifndef MEOW`. Closes #132561.
1 parent fcdb91e commit 9eb5843

File tree

8 files changed

+315
-0
lines changed

8 files changed

+315
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ add_clang_library(clangTidyModernizeModule STATIC
3030
UnaryStaticAssertCheck.cpp
3131
UseAutoCheck.cpp
3232
UseBoolLiteralsCheck.cpp
33+
UseConcisePreprocessorDirectivesCheck.cpp
3334
UseConstraintsCheck.cpp
3435
UseDefaultMemberInitCheck.cpp
3536
UseDesignatedInitializersCheck.cpp

clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "UnaryStaticAssertCheck.h"
3232
#include "UseAutoCheck.h"
3333
#include "UseBoolLiteralsCheck.h"
34+
#include "UseConcisePreprocessorDirectivesCheck.h"
3435
#include "UseConstraintsCheck.h"
3536
#include "UseDefaultMemberInitCheck.h"
3637
#include "UseDesignatedInitializersCheck.h"
@@ -76,6 +77,8 @@ class ModernizeModule : public ClangTidyModule {
7677
CheckFactories.registerCheck<MinMaxUseInitializerListCheck>(
7778
"modernize-min-max-use-initializer-list");
7879
CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
80+
CheckFactories.registerCheck<UseConcisePreprocessorDirectivesCheck>(
81+
"modernize-use-concise-preprocessor-directives");
7982
CheckFactories.registerCheck<UseDesignatedInitializersCheck>(
8083
"modernize-use-designated-initializers");
8184
CheckFactories.registerCheck<UseIntegerSignComparisonCheck>(
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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+
namespace clang::tidy::modernize {
16+
17+
namespace {
18+
19+
class IfPreprocessorCallbacks final : public PPCallbacks {
20+
public:
21+
IfPreprocessorCallbacks(ClangTidyCheck &Check, Preprocessor &PP)
22+
: Check(Check), PP(PP) {}
23+
24+
void If(SourceLocation Loc, SourceRange ConditionRange,
25+
ConditionValueKind) override {
26+
impl(Loc, ConditionRange, {"ifdef", "ifndef"});
27+
}
28+
29+
void Elif(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind,
30+
SourceLocation) override {
31+
if (PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus23) {
32+
impl(Loc, ConditionRange, {"elifdef", "elifndef"});
33+
}
34+
}
35+
36+
private:
37+
void impl(SourceLocation DirectiveLoc, SourceRange ConditionRange,
38+
const llvm::StringLiteral (&Replacements)[2]) {
39+
StringRef Condition =
40+
Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange),
41+
PP.getSourceManager(), PP.getLangOpts());
42+
Lexer Lex(DirectiveLoc, PP.getLangOpts(), Condition.data(),
43+
Condition.data(), Condition.data() + Condition.size());
44+
Token Tok;
45+
bool Inverted = false; // The inverted form of #*def is #*ndef.
46+
std::size_t ParensNestingDepth = 0;
47+
for (;;) {
48+
if (Lex.LexFromRawLexer(Tok))
49+
return;
50+
51+
if (Tok.is(tok::TokenKind::exclaim) ||
52+
(PP.getLangOpts().CPlusPlus &&
53+
Tok.is(tok::TokenKind::raw_identifier) &&
54+
Tok.getRawIdentifier() == "not"))
55+
Inverted = !Inverted;
56+
else if (Tok.is(tok::TokenKind::l_paren))
57+
++ParensNestingDepth;
58+
else
59+
break;
60+
}
61+
62+
if (Tok.isNot(tok::TokenKind::raw_identifier) ||
63+
Tok.getRawIdentifier() != "defined")
64+
return;
65+
66+
bool NoMoreTokens = Lex.LexFromRawLexer(Tok);
67+
if (Tok.is(tok::TokenKind::l_paren)) {
68+
if (NoMoreTokens)
69+
return;
70+
++ParensNestingDepth;
71+
NoMoreTokens = Lex.LexFromRawLexer(Tok);
72+
}
73+
74+
if (Tok.isNot(tok::TokenKind::raw_identifier))
75+
return;
76+
StringRef Macro = Tok.getRawIdentifier();
77+
78+
while (!NoMoreTokens) {
79+
NoMoreTokens = Lex.LexFromRawLexer(Tok);
80+
if (Tok.isNot(tok::TokenKind::r_paren))
81+
return;
82+
--ParensNestingDepth;
83+
}
84+
85+
if (ParensNestingDepth != 0)
86+
return;
87+
88+
Check.diag(DirectiveLoc,
89+
"preprocessor condition can be written more concisely")
90+
<< FixItHint::CreateReplacement(DirectiveLoc, Replacements[Inverted])
91+
<< FixItHint::CreateReplacement(ConditionRange, Macro);
92+
}
93+
94+
ClangTidyCheck &Check;
95+
const Preprocessor &PP;
96+
};
97+
98+
} // namespace
99+
100+
void UseConcisePreprocessorDirectivesCheck::registerPPCallbacks(
101+
const SourceManager &, Preprocessor *PP, Preprocessor *) {
102+
PP->addPPCallbacks(std::make_unique<IfPreprocessorCallbacks>(*this, *PP));
103+
}
104+
105+
} // namespace clang::tidy::modernize
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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_MODERNIZE_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::modernize {
15+
16+
/// Shortens `#if` preprocessor conditions:
17+
///
18+
/// #if defined(MEOW) -> #ifdef MEOW
19+
/// #if !defined(MEOW) -> #ifndef MEOW
20+
///
21+
/// And, since C23 and C++23, shortens `#elif` conditions too:
22+
///
23+
/// #elif defined(MEOW) -> #elifdef MEOW
24+
/// #elif !defined(MEOW) -> #elifndef MEOW
25+
///
26+
/// User-facing documentation:
27+
/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-concise-preprocessor-directives.html
28+
class UseConcisePreprocessorDirectivesCheck : public ClangTidyCheck {
29+
public:
30+
using ClangTidyCheck::ClangTidyCheck;
31+
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
32+
Preprocessor *ModuleExpanderPP) override;
33+
};
34+
35+
} // namespace clang::tidy::modernize
36+
37+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONCISEPREPROCESSORDIRECTIVESCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ New checks
142142
Finds unscoped (non-class) ``enum`` declarations and suggests using
143143
``enum class`` instead.
144144

145+
- New :doc:`modernize-use-concise-preprocessor-directives
146+
<clang-tidy/checks/modernize/use-concise-preprocessor-directives>` check.
147+
148+
Rewrites preprocessor conditions like ``#if defined(MEOW)`` as ``#ifdef MEOW``
149+
and ``#elif !defined(MEOW)`` as ``#elifndef MEOW``.
150+
145151
- New :doc:`modernize-use-scoped-lock
146152
<clang-tidy/checks/modernize/use-scoped-lock>` check.
147153

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

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

0 commit comments

Comments
 (0)