1 //===--- AvoidUnconditionalPreprocessorIfCheck.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 "AvoidUnconditionalPreprocessorIfCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/Lex/PPCallbacks.h" 12 #include "clang/Lex/Preprocessor.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang::tidy::readability { 17 18 namespace { 19 struct AvoidUnconditionalPreprocessorIfPPCallbacks : public PPCallbacks { 20 21 explicit AvoidUnconditionalPreprocessorIfPPCallbacks(ClangTidyCheck &Check, 22 Preprocessor &PP) 23 : Check(Check), PP(PP) {} 24 25 void If(SourceLocation Loc, SourceRange ConditionRange, 26 ConditionValueKind ConditionValue) override { 27 if (ConditionValue == CVK_NotEvaluated) 28 return; 29 SourceManager &SM = PP.getSourceManager(); 30 if (!isImmutable(SM, PP.getLangOpts(), ConditionRange)) 31 return; 32 33 if (ConditionValue == CVK_True) 34 Check.diag(Loc, "preprocessor condition is always 'true', consider " 35 "removing condition but leaving its contents"); 36 else 37 Check.diag(Loc, "preprocessor condition is always 'false', consider " 38 "removing both the condition and its contents"); 39 } 40 41 bool isImmutable(SourceManager &SM, const LangOptions &LangOpts, 42 SourceRange ConditionRange) { 43 SourceLocation Loc = ConditionRange.getBegin(); 44 if (Loc.isMacroID()) 45 return false; 46 47 Token Tok; 48 if (Lexer::getRawToken(Loc, Tok, SM, LangOpts, true)) { 49 std::optional<Token> TokOpt = Lexer::findNextToken(Loc, SM, LangOpts); 50 if (!TokOpt || TokOpt->getLocation().isMacroID()) 51 return false; 52 Tok = *TokOpt; 53 } 54 55 while (Tok.getLocation() <= ConditionRange.getEnd()) { 56 if (!isImmutableToken(Tok)) 57 return false; 58 59 std::optional<Token> TokOpt = 60 Lexer::findNextToken(Tok.getLocation(), SM, LangOpts); 61 if (!TokOpt || TokOpt->getLocation().isMacroID()) 62 return false; 63 Tok = *TokOpt; 64 } 65 66 return true; 67 } 68 69 bool isImmutableToken(const Token &Tok) { 70 switch (Tok.getKind()) { 71 case tok::eod: 72 case tok::eof: 73 case tok::numeric_constant: 74 case tok::char_constant: 75 case tok::wide_char_constant: 76 case tok::utf8_char_constant: 77 case tok::utf16_char_constant: 78 case tok::utf32_char_constant: 79 case tok::string_literal: 80 case tok::wide_string_literal: 81 case tok::comment: 82 return true; 83 case tok::raw_identifier: 84 return (Tok.getRawIdentifier() == "true" || 85 Tok.getRawIdentifier() == "false"); 86 default: 87 return Tok.getKind() >= tok::l_square && 88 Tok.getKind() <= tok::greatergreatergreater; 89 } 90 } 91 92 ClangTidyCheck &Check; 93 Preprocessor &PP; 94 }; 95 96 } // namespace 97 98 void AvoidUnconditionalPreprocessorIfCheck::registerPPCallbacks( 99 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { 100 PP->addPPCallbacks( 101 std::make_unique<AvoidUnconditionalPreprocessorIfPPCallbacks>(*this, 102 *PP)); 103 } 104 105 } // namespace clang::tidy::readability 106