- 
                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 9 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
    
  
  
    
              
        
          
          
            108 changes: 108 additions & 0 deletions
          
          108 
        
  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,108 @@ | ||
| //===--- 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::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 llvm::StringLiteral (&Replacements)[2]) { | ||
                
      
                  localspook marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| // 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") | ||
                
      
                  localspook marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| << 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 | ||
        
          
          
            40 changes: 40 additions & 0 deletions
          
          40 
        
  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,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_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USECONCISEPREPROCESSORDIRECTIVESCHECK_H | ||
| 
     | 
||
| #include "../ClangTidyCheck.h" | ||
| 
     | 
||
| namespace clang::tidy::readability { | ||
| 
     | 
||
| /// 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 | ||
                
      
                  localspook marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| /// | ||
| /// 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 LangOpts.CPlusPlus || LangOpts.C99; | ||
                
      
                  localspook marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| } | ||
| }; | ||
| 
     | 
||
| } // 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 | 
        
          
          
            138 changes: 138 additions & 0 deletions
          
          138 
        
  ...-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,138 @@ | ||
| // RUN: %check_clang_tidy -std=c++98 -check-suffixes=,CXX %s readability-use-concise-preprocessor-directives %t | ||
| // RUN: %check_clang_tidy -std=c++11 -check-suffixes=,CXX %s readability-use-concise-preprocessor-directives %t | ||
| // RUN: %check_clang_tidy -std=c++14 -check-suffixes=,CXX %s readability-use-concise-preprocessor-directives %t | ||
| // RUN: %check_clang_tidy -std=c++17 -check-suffixes=,CXX %s readability-use-concise-preprocessor-directives %t | ||
| // RUN: %check_clang_tidy -std=c++20 -check-suffixes=,CXX %s readability-use-concise-preprocessor-directives %t | ||
                
      
                  localspook marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| // 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 %s readability-use-concise-preprocessor-directives %t -- -- -x c | ||
| // RUN: %check_clang_tidy -std=c11 %s readability-use-concise-preprocessor-directives %t -- -- -x c | ||
                
      
                  localspook marked this conversation as resolved.
               
              
                Outdated
          
            Show resolved
            Hide resolved
         | 
||
| // 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))) | ||
                
      
                  localspook marked this conversation as resolved.
               
          
            Show resolved
            Hide resolved
         | 
||
| // 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 | ||
  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.