- 
                Notifications
    
You must be signed in to change notification settings  - Fork 15.1k
 
          [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
      
      
    
  
     Merged
                    Changes from 1 commit
      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
    
  
  
    
              
        
          
          
            105 changes: 105 additions & 0 deletions
          
          105 
        
  clang-tools-extra/clang-tidy/modernize/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,105 @@ | ||
| //===--- 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, 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]) { | ||
| StringRef Condition = | ||
| Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange), | ||
| PP.getSourceManager(), PP.getLangOpts()); | ||
| Lexer Lex(DirectiveLoc, PP.getLangOpts(), Condition.data(), | ||
| Condition.data(), Condition.data() + Condition.size()); | ||
| 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; | ||
| 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") | ||
                
      
                  localspook marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| << 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 | ||
        
          
          
            37 changes: 37 additions & 0 deletions
          
          37 
        
  clang-tools-extra/clang-tidy/modernize/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,37 @@ | ||
| //===--- 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; | ||
| }; | ||
                
      
                  localspook marked this conversation as resolved.
               
          
            Show resolved
            Hide resolved
         | 
||
| 
     | 
||
| } // namespace clang::tidy::modernize | ||
| 
     | 
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_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
    
  
  
    
              
        
          
          
            28 changes: 28 additions & 0 deletions
          
          28 
        
  ...-extra/docs/clang-tidy/checks/modernize/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,28 @@ | ||
| .. title:: clang-tidy - modernize-use-concise-preprocessor-directives | ||
| 
     | 
||
| modernize-use-concise-preprocessor-directives | ||
| ============================================= | ||
| 
     | 
||
| Shortens `#if` preprocessor conditions: | ||
                
      
                  localspook marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| 
     | 
||
| .. code-block:: c++ | ||
| 
     | 
||
| #if defined(MEOW) | ||
| #if !defined(MEOW) | ||
| 
     | 
||
| // becomes | ||
| 
     | 
||
| #ifdef MEOW | ||
| #ifndef MEOW | ||
| 
     | 
||
| And, since C23 and C++23, shortens `#elif` conditions too: | ||
| 
     | 
||
| .. code-block:: c++ | ||
| 
     | 
||
| #elif defined(MEOW) | ||
| #elif !defined(MEOW) | ||
| 
     | 
||
| // becomes | ||
| 
     | 
||
| #elifdef MEOW | ||
| #elifndef MEOW | ||
        
          
          
            134 changes: 134 additions & 0 deletions
          
          134 
        
  clang-tools-extra/test/clang-tidy/checkers/modernize/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,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 | 
  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.