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
14using namespace clang::ast_matchers;
15
16namespace clang::tidy::readability {
17
18namespace {
19struct 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, LangOpts: PP.getLangOpts(), ConditionRange))
31 return;
32
33 if (ConditionValue == CVK_True)
34 Check.diag(Loc, Description: "preprocessor condition is always 'true', consider "
35 "removing condition but leaving its contents");
36 else
37 Check.diag(Loc, Description: "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, Result&: Tok, SM, LangOpts, IgnoreWhiteSpace: 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(Loc: 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 && Tok.getKind() <= tok::caretcaret;
88 }
89 }
90
91 ClangTidyCheck &Check;
92 Preprocessor &PP;
93};
94
95} // namespace
96
97void AvoidUnconditionalPreprocessorIfCheck::registerPPCallbacks(
98 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
99 PP->addPPCallbacks(
100 C: std::make_unique<AvoidUnconditionalPreprocessorIfPPCallbacks>(args&: *this,
101 args&: *PP));
102}
103
104} // namespace clang::tidy::readability
105

source code of clang-tools-extra/clang-tidy/readability/AvoidUnconditionalPreprocessorIfCheck.cpp