-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[clang-tidy] Add new check: readability-use-concise-preprocessor-directives
#146830
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
Merged
+330
−0
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
9eb5843
[clang-tidy] Add new check: `modernize-use-concise-preprocessor-direc…
localspook 8e1c675
Specify `isLanguageVersionSupported`
localspook 7d42a2f
Const correctness
localspook 43de457
Align release notes and docs
localspook 3ef8a8d
Fix assert failure
localspook ce0e935
Update message, add test
localspook 8bad406
Make tests more readable
localspook 36719c8
Move to `readability` category
localspook 212e5e4
Fix docs
localspook a2d0a59
Address feedback
localspook 491d304
Use `std::array`
localspook 8a85495
Enable for Objective-C/C++
localspook File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
//===--- 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" | ||
|
||
#include <array> | ||
|
||
namespace clang::tidy::readability { | ||
|
||
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 std::array<llvm::StringLiteral, 2> &Replacements) { | ||
// 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 using '#%0'") | ||
<< FixItHint::CreateReplacement(DirectiveLoc, Replacements[Inverted]) | ||
<< FixItHint::CreateReplacement(ConditionRange, Macro) | ||
<< Replacements[Inverted]; | ||
} | ||
|
||
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::readability |
34 changes: 34 additions & 0 deletions
34
clang-tools-extra/clang-tidy/readability/UseConcisePreprocessorDirectivesCheck.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
//===--- 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_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H | ||
|
||
#include "../ClangTidyCheck.h" | ||
|
||
namespace clang::tidy::readability { | ||
|
||
/// Finds uses of ``#if`` that can be simplified to ``#ifdef`` or ``#ifndef`` | ||
/// and, since C23 and C++23, uses of ``#elif`` that can be simplified to | ||
/// ``#elifdef`` or ``#elifndef``. | ||
/// | ||
/// User-facing documentation: | ||
/// https://clang.llvm.org/extra/clang-tidy/checks/readability/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 true; | ||
} | ||
}; | ||
|
||
} // namespace clang::tidy::readability | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
...xtra/docs/clang-tidy/checks/readability/use-concise-preprocessor-directives.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
.. title:: clang-tidy - readability-use-concise-preprocessor-directives | ||
|
||
readability-use-concise-preprocessor-directives | ||
=============================================== | ||
|
||
Finds uses of ``#if`` that can 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 |
144 changes: 144 additions & 0 deletions
144
...-tools-extra/test/clang-tidy/checkers/readability/use-concise-preprocessor-directives.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// 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 | ||
// RUN: %check_clang_tidy -std=c++23-or-later -check-suffixes=,23,CXX,CXX23 %s readability-use-concise-preprocessor-directives %t | ||
|
||
// RUN: %check_clang_tidy -std=c99,c11,c17 %s readability-use-concise-preprocessor-directives %t -- -- -x c | ||
// RUN: %check_clang_tidy -std=c23-or-later -check-suffixes=,23 %s readability-use-concise-preprocessor-directives %t -- -- -x c | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifdef FOO | ||
#if defined(FOO) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: #elifdef BAR | ||
#elif defined(BAR) | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifdef FOO | ||
#if defined FOO | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: #elifdef BAR | ||
#elif defined BAR | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifdef FOO | ||
#if (defined(FOO)) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: # elifdef BAR | ||
# elif (defined(BAR)) | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifdef FOO | ||
#if (defined FOO) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:4: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: # elifdef BAR | ||
# elif (defined BAR) | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if !defined(FOO) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-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 using '#ifndef' [readability-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 using '#elifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-CXX23: #elifndef BAR | ||
#elif not defined(BAR) | ||
#endif | ||
#endif // __cplusplus | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if !defined FOO | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-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 using '#ifndef' [readability-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 using '#elifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-CXX23: #elifndef BAR | ||
#elif not defined BAR | ||
#endif | ||
#endif // __cplusplus | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if (!defined(FOO)) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: #elifndef BAR | ||
#elif (!defined(BAR)) | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if (!defined FOO) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: #elifndef BAR | ||
#elif (!defined BAR) | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if !(defined(FOO)) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: #elifndef BAR | ||
#elif !(defined(BAR)) | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if !(defined FOO) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifndef' [readability-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: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if !((!!(defined(FOO)))) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: #elifdef BAR | ||
#elif ((!(!(defined(BAR))))) | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if !((!!(defined FOO))) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: #elifdef BAR | ||
#elif ((!(!(defined BAR)))) | ||
#endif | ||
|
||
// CHECK-MESSAGES: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#ifndef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES: #ifndef FOO | ||
#if !( (!! ( defined FOO )) ) | ||
// CHECK-MESSAGES-23: :[[@LINE+2]]:2: warning: preprocessor condition can be written more concisely using '#elifdef' [readability-use-concise-preprocessor-directives] | ||
// CHECK-FIXES-23: #elifdef BAR | ||
#elif ( ( !(!( defined BAR) ) )) | ||
#endif | ||
|
||
#if FOO | ||
#elif BAR | ||
#endif | ||
|
||
#if defined(FOO) && defined(BAR) | ||
#elif defined(FOO) && defined(BAR) | ||
#endif | ||
|
||
#if defined FOO && BAR | ||
#endif |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.