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