152296f5eSPiotr Zegar //===--- AvoidUnconditionalPreprocessorIfCheck.cpp - clang-tidy -----------===//
252296f5eSPiotr Zegar //
352296f5eSPiotr Zegar // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
452296f5eSPiotr Zegar // See https://llvm.org/LICENSE.txt for license information.
552296f5eSPiotr Zegar // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
652296f5eSPiotr Zegar //
752296f5eSPiotr Zegar //===----------------------------------------------------------------------===//
852296f5eSPiotr Zegar 
952296f5eSPiotr Zegar #include "AvoidUnconditionalPreprocessorIfCheck.h"
1052296f5eSPiotr Zegar #include "clang/AST/ASTContext.h"
1152296f5eSPiotr Zegar #include "clang/Lex/PPCallbacks.h"
1252296f5eSPiotr Zegar #include "clang/Lex/Preprocessor.h"
1352296f5eSPiotr Zegar 
1452296f5eSPiotr Zegar using namespace clang::ast_matchers;
1552296f5eSPiotr Zegar 
1652296f5eSPiotr Zegar namespace clang::tidy::readability {
1752296f5eSPiotr Zegar 
1852296f5eSPiotr Zegar namespace {
1952296f5eSPiotr Zegar struct AvoidUnconditionalPreprocessorIfPPCallbacks : public PPCallbacks {
2052296f5eSPiotr Zegar 
2152296f5eSPiotr Zegar   explicit AvoidUnconditionalPreprocessorIfPPCallbacks(ClangTidyCheck &Check,
2252296f5eSPiotr Zegar                                                        Preprocessor &PP)
2352296f5eSPiotr Zegar       : Check(Check), PP(PP) {}
2452296f5eSPiotr Zegar 
2552296f5eSPiotr Zegar   void If(SourceLocation Loc, SourceRange ConditionRange,
2652296f5eSPiotr Zegar           ConditionValueKind ConditionValue) override {
2752296f5eSPiotr Zegar     if (ConditionValue == CVK_NotEvaluated)
2852296f5eSPiotr Zegar       return;
2952296f5eSPiotr Zegar     SourceManager &SM = PP.getSourceManager();
3052296f5eSPiotr Zegar     if (!isImmutable(SM, PP.getLangOpts(), ConditionRange))
3152296f5eSPiotr Zegar       return;
3252296f5eSPiotr Zegar 
3352296f5eSPiotr Zegar     if (ConditionValue == CVK_True)
3452296f5eSPiotr Zegar       Check.diag(Loc, "preprocessor condition is always 'true', consider "
3552296f5eSPiotr Zegar                       "removing condition but leaving its contents");
3652296f5eSPiotr Zegar     else
3752296f5eSPiotr Zegar       Check.diag(Loc, "preprocessor condition is always 'false', consider "
3852296f5eSPiotr Zegar                       "removing both the condition and its contents");
3952296f5eSPiotr Zegar   }
4052296f5eSPiotr Zegar 
4152296f5eSPiotr Zegar   bool isImmutable(SourceManager &SM, const LangOptions &LangOpts,
4252296f5eSPiotr Zegar                    SourceRange ConditionRange) {
4352296f5eSPiotr Zegar     SourceLocation Loc = ConditionRange.getBegin();
4452296f5eSPiotr Zegar     if (Loc.isMacroID())
4552296f5eSPiotr Zegar       return false;
4652296f5eSPiotr Zegar 
4752296f5eSPiotr Zegar     Token Tok;
4852296f5eSPiotr Zegar     if (Lexer::getRawToken(Loc, Tok, SM, LangOpts, true)) {
4952296f5eSPiotr Zegar       std::optional<Token> TokOpt = Lexer::findNextToken(Loc, SM, LangOpts);
5052296f5eSPiotr Zegar       if (!TokOpt || TokOpt->getLocation().isMacroID())
5152296f5eSPiotr Zegar         return false;
5252296f5eSPiotr Zegar       Tok = *TokOpt;
5352296f5eSPiotr Zegar     }
5452296f5eSPiotr Zegar 
5552296f5eSPiotr Zegar     while (Tok.getLocation() <= ConditionRange.getEnd()) {
5652296f5eSPiotr Zegar       if (!isImmutableToken(Tok))
5752296f5eSPiotr Zegar         return false;
5852296f5eSPiotr Zegar 
5952296f5eSPiotr Zegar       std::optional<Token> TokOpt =
6052296f5eSPiotr Zegar           Lexer::findNextToken(Tok.getLocation(), SM, LangOpts);
6152296f5eSPiotr Zegar       if (!TokOpt || TokOpt->getLocation().isMacroID())
6252296f5eSPiotr Zegar         return false;
6352296f5eSPiotr Zegar       Tok = *TokOpt;
6452296f5eSPiotr Zegar     }
6552296f5eSPiotr Zegar 
6652296f5eSPiotr Zegar     return true;
6752296f5eSPiotr Zegar   }
6852296f5eSPiotr Zegar 
6952296f5eSPiotr Zegar   bool isImmutableToken(const Token &Tok) {
7052296f5eSPiotr Zegar     switch (Tok.getKind()) {
7152296f5eSPiotr Zegar     case tok::eod:
7252296f5eSPiotr Zegar     case tok::eof:
7352296f5eSPiotr Zegar     case tok::numeric_constant:
7452296f5eSPiotr Zegar     case tok::char_constant:
7552296f5eSPiotr Zegar     case tok::wide_char_constant:
7652296f5eSPiotr Zegar     case tok::utf8_char_constant:
7752296f5eSPiotr Zegar     case tok::utf16_char_constant:
7852296f5eSPiotr Zegar     case tok::utf32_char_constant:
7952296f5eSPiotr Zegar     case tok::string_literal:
8052296f5eSPiotr Zegar     case tok::wide_string_literal:
8152296f5eSPiotr Zegar     case tok::comment:
8252296f5eSPiotr Zegar       return true;
8352296f5eSPiotr Zegar     case tok::raw_identifier:
8452296f5eSPiotr Zegar       return (Tok.getRawIdentifier() == "true" ||
8552296f5eSPiotr Zegar               Tok.getRawIdentifier() == "false");
8652296f5eSPiotr Zegar     default:
87*1881f648SAaron Ballman       return Tok.getKind() >= tok::l_square &&
88*1881f648SAaron Ballman              Tok.getKind() <= tok::greatergreatergreater;
8952296f5eSPiotr Zegar     }
9052296f5eSPiotr Zegar   }
9152296f5eSPiotr Zegar 
9252296f5eSPiotr Zegar   ClangTidyCheck &Check;
9352296f5eSPiotr Zegar   Preprocessor &PP;
9452296f5eSPiotr Zegar };
9552296f5eSPiotr Zegar 
9652296f5eSPiotr Zegar } // namespace
9752296f5eSPiotr Zegar 
9852296f5eSPiotr Zegar void AvoidUnconditionalPreprocessorIfCheck::registerPPCallbacks(
9952296f5eSPiotr Zegar     const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
10052296f5eSPiotr Zegar   PP->addPPCallbacks(
10152296f5eSPiotr Zegar       std::make_unique<AvoidUnconditionalPreprocessorIfPPCallbacks>(*this,
10252296f5eSPiotr Zegar                                                                     *PP));
10352296f5eSPiotr Zegar }
10452296f5eSPiotr Zegar 
10552296f5eSPiotr Zegar } // namespace clang::tidy::readability
106