Skip to content
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UnaryStaticAssertCheck.cpp
UseAutoCheck.cpp
UseBoolLiteralsCheck.cpp
UseConcisePreprocessorDirectivesCheck.cpp
UseConstraintsCheck.cpp
UseDefaultMemberInitCheck.cpp
UseDesignatedInitializersCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "UnaryStaticAssertCheck.h"
#include "UseAutoCheck.h"
#include "UseBoolLiteralsCheck.h"
#include "UseConcisePreprocessorDirectivesCheck.h"
#include "UseConstraintsCheck.h"
#include "UseDefaultMemberInitCheck.h"
#include "UseDesignatedInitializersCheck.h"
Expand Down Expand Up @@ -76,6 +77,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<MinMaxUseInitializerListCheck>(
"modernize-min-max-use-initializer-list");
CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
CheckFactories.registerCheck<UseConcisePreprocessorDirectivesCheck>(
"modernize-use-concise-preprocessor-directives");
CheckFactories.registerCheck<UseDesignatedInitializersCheck>(
"modernize-use-designated-initializers");
CheckFactories.registerCheck<UseIntegerSignComparisonCheck>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//===--- UseConcisePreprocessorDirectivesCheck.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 "UseConcisePreprocessorDirectivesCheck.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Preprocessor.h"

namespace clang::tidy::modernize {

namespace {

class IfPreprocessorCallbacks final : public PPCallbacks {
public:
IfPreprocessorCallbacks(ClangTidyCheck &Check, const Preprocessor &PP)
: Check(Check), PP(PP) {}

void If(SourceLocation Loc, SourceRange ConditionRange,
ConditionValueKind) override {
impl(Loc, ConditionRange, {"ifdef", "ifndef"});
}

void Elif(SourceLocation Loc, SourceRange ConditionRange, ConditionValueKind,
SourceLocation) override {
if (PP.getLangOpts().C23 || PP.getLangOpts().CPlusPlus23) {
impl(Loc, ConditionRange, {"elifdef", "elifndef"});
}
}

private:
void impl(SourceLocation DirectiveLoc, SourceRange ConditionRange,
const llvm::StringLiteral (&Replacements)[2]) {
// Lexer requires its input range to be null-terminated.
SmallString<128> Condition =
Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange),
PP.getSourceManager(), PP.getLangOpts());
Condition.push_back('\0');
Lexer Lex(DirectiveLoc, PP.getLangOpts(), Condition.data(),
Condition.data(), Condition.data() + Condition.size() - 1);
Token Tok;
bool Inverted = false; // The inverted form of #*def is #*ndef.
std::size_t ParensNestingDepth = 0;
for (;;) {
if (Lex.LexFromRawLexer(Tok))
return;

if (Tok.is(tok::TokenKind::exclaim) ||
(PP.getLangOpts().CPlusPlus &&
Tok.is(tok::TokenKind::raw_identifier) &&
Tok.getRawIdentifier() == "not"))
Inverted = !Inverted;
else if (Tok.is(tok::TokenKind::l_paren))
++ParensNestingDepth;
else
break;
}

if (Tok.isNot(tok::TokenKind::raw_identifier) ||
Tok.getRawIdentifier() != "defined")
return;

bool NoMoreTokens = Lex.LexFromRawLexer(Tok);
if (Tok.is(tok::TokenKind::l_paren)) {
if (NoMoreTokens)
return;
++ParensNestingDepth;
NoMoreTokens = Lex.LexFromRawLexer(Tok);
}

if (Tok.isNot(tok::TokenKind::raw_identifier))
return;
const StringRef Macro = Tok.getRawIdentifier();

while (!NoMoreTokens) {
NoMoreTokens = Lex.LexFromRawLexer(Tok);
if (Tok.isNot(tok::TokenKind::r_paren))
return;
--ParensNestingDepth;
}

if (ParensNestingDepth != 0)
return;

Check.diag(DirectiveLoc,
"preprocessor condition can be written more concisely")
<< FixItHint::CreateReplacement(DirectiveLoc, Replacements[Inverted])
<< FixItHint::CreateReplacement(ConditionRange, Macro);
}

ClangTidyCheck &Check;
const Preprocessor &PP;
};

} // namespace

void UseConcisePreprocessorDirectivesCheck::registerPPCallbacks(
const SourceManager &, Preprocessor *PP, Preprocessor *) {
PP->addPPCallbacks(std::make_unique<IfPreprocessorCallbacks>(*this, *PP));
}

} // namespace clang::tidy::modernize
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===--- UseConcisePreprocessorDirectivesCheck.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_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONCISEPREPROCESSORDIRECTIVESCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::modernize {

/// Shortens `#if` preprocessor conditions:
///
/// #if defined(MEOW) -> #ifdef MEOW
/// #if !defined(MEOW) -> #ifndef MEOW
///
/// And, since C23 and C++23, shortens `#elif` conditions too:
///
/// #elif defined(MEOW) -> #elifdef MEOW
/// #elif !defined(MEOW) -> #elifndef MEOW
///
/// User-facing documentation:
/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-concise-preprocessor-directives.html
class UseConcisePreprocessorDirectivesCheck : public ClangTidyCheck {
public:
using ClangTidyCheck::ClangTidyCheck;
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
Preprocessor *ModuleExpanderPP) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus || LangOpts.C99;
}
};

} // namespace clang::tidy::modernize

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USECONCISEPREPROCESSORDIRECTIVESCHECK_H
7 changes: 7 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ New checks
Finds unscoped (non-class) ``enum`` declarations and suggests using
``enum class`` instead.

- New :doc:`modernize-use-concise-preprocessor-directives
<clang-tidy/checks/modernize/use-concise-preprocessor-directives>` check.

Finds uses of ``#if`` that be simplified to ``#ifdef`` or ``#ifndef`` and,
since C23 and C++23, uses of ``#elif`` that can be simplified to ``#elifdef``
or ``#elifndef``.

- New :doc:`modernize-use-scoped-lock
<clang-tidy/checks/modernize/use-scoped-lock>` check.

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ Clang-Tidy Checks
:doc:`modernize-unary-static-assert <modernize/unary-static-assert>`, "Yes"
:doc:`modernize-use-auto <modernize/use-auto>`, "Yes"
:doc:`modernize-use-bool-literals <modernize/use-bool-literals>`, "Yes"
:doc:`modernize-use-concise-preprocessor-directives <modernize/use-concise-preprocessor-directives>`, "Yes"
:doc:`modernize-use-constraints <modernize/use-constraints>`, "Yes"
:doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
:doc:`modernize-use-designated-initializers <modernize/use-designated-initializers>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.. title:: clang-tidy - modernize-use-concise-preprocessor-directives

modernize-use-concise-preprocessor-directives
=============================================

Finds uses of ``#if`` that be simplified to ``#ifdef`` or ``#ifndef`` and,
since C23 and C++23, uses of ``#elif`` that can be simplified to ``#elifdef``
or ``#elifndef``:

.. code-block:: c++

#if defined(MEOW)
#if !defined(MEOW)

// becomes

#ifdef MEOW
#ifndef MEOW

Since C23 and C++23:

.. code-block:: c++

#elif defined(MEOW)
#elif !defined(MEOW)

// becomes

#elifdef MEOW
#elifndef MEOW
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// RUN: %check_clang_tidy -std=c++98 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
// RUN: %check_clang_tidy -std=c++11 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
// RUN: %check_clang_tidy -std=c++14 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
// RUN: %check_clang_tidy -std=c++17 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
// RUN: %check_clang_tidy -std=c++20 -check-suffixes=ALL,CXX %s modernize-use-concise-preprocessor-directives %t
// RUN: %check_clang_tidy -std=c++23-or-later -check-suffixes=ALL,23,CXX,CXX23 %s modernize-use-concise-preprocessor-directives %t

// RUN: %check_clang_tidy -std=c99 -check-suffix=ALL %s modernize-use-concise-preprocessor-directives %t -- -- -x c
// RUN: %check_clang_tidy -std=c11 -check-suffix=ALL %s modernize-use-concise-preprocessor-directives %t -- -- -x c
// RUN: %check_clang_tidy -std=c23-or-later -check-suffix=ALL,23 %s modernize-use-concise-preprocessor-directives %t -- -- -x c

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifdef FOO
#if defined(FOO)
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifdef BAR
#elif defined(BAR)
#endif

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifdef FOO
#if defined FOO
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifdef BAR
#elif defined BAR
#endif

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifdef FOO
#if (defined(FOO))
// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: # elifdef BAR
# elif (defined(BAR))
#endif

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifdef FOO
#if (defined FOO)
// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: # elifdef BAR
# elif (defined BAR)
#endif

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifndef FOO
#if !defined(FOO)
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifndef BAR
#elif !defined(BAR)
#endif

#ifdef __cplusplus
// CHECK-MESSAGES-CXX: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-CXX: #ifndef FOO
#if not defined(FOO)
// CHECK-MESSAGES-CXX23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-CXX23: #elifndef BAR
#elif not defined(BAR)
#endif
#endif // __cplusplus

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifndef FOO
#if !defined FOO
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifndef BAR
#elif !defined BAR
#endif

#ifdef __cplusplus
// CHECK-MESSAGES-CXX: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-CXX: #ifndef FOO
#if not defined FOO
// CHECK-MESSAGES-CXX23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-CXX23: #elifndef BAR
#elif not defined BAR
#endif
#endif // __cplusplus

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifndef FOO
#if (!defined(FOO))
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifndef BAR
#elif (!defined(BAR))
#endif

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifndef FOO
#if (!defined FOO)
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifndef BAR
#elif (!defined BAR)
#endif

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifndef FOO
#if !(defined(FOO))
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifndef BAR
#elif !(defined(BAR))
#endif

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifndef FOO
#if !(defined FOO)
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifndef BAR
#elif !(defined BAR)
#endif

// These cases with many parentheses and negations are unrealistic, but
// handling them doesn't really add any complexity to the implementation.
// Test them for good measure.

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifndef FOO
#if !((!!(defined(FOO))))
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifdef BAR
#elif ((!(!(defined(BAR)))))
#endif

// CHECK-MESSAGES-ALL: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-ALL: #ifndef FOO
#if !((!!(defined FOO)))
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely [modernize-use-concise-preprocessor-directives]
// CHECK-FIXES-23: #elifdef BAR
#elif ((!(!(defined BAR))))
#endif

#if FOO
#elif BAR
#endif