reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
//===--- RedundantPreprocessorCheck.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 "RedundantPreprocessorCheck.h"
#include "clang/Frontend/CompilerInstance.h"

namespace clang {
namespace tidy {
namespace readability {

namespace {
/// Information about an opening preprocessor directive.
struct PreprocessorEntry {
  SourceLocation Loc;
  /// Condition used after the preprocessor directive.
  std::string Condition;
};

class RedundantPreprocessorCallbacks : public PPCallbacks {
  enum DirectiveKind { DK_If = 0, DK_Ifdef = 1, DK_Ifndef = 2 };

public:
  explicit RedundantPreprocessorCallbacks(ClangTidyCheck &Check,
                                          Preprocessor &PP)
      : Check(Check), PP(PP),
        WarningDescription("nested redundant %select{#if|#ifdef|#ifndef}0; "
                           "consider removing it"),
        NoteDescription("previous %select{#if|#ifdef|#ifndef}0 was here") {}

  void If(SourceLocation Loc, SourceRange ConditionRange,
          ConditionValueKind ConditionValue) override {
    StringRef Condition =
        Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange),
                             PP.getSourceManager(), PP.getLangOpts());
    CheckMacroRedundancy(Loc, Condition, IfStack, DK_If, DK_If, true);
  }

  void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
             const MacroDefinition &MacroDefinition) override {
    std::string MacroName = PP.getSpelling(MacroNameTok);
    CheckMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifdef, DK_Ifdef, true);
    CheckMacroRedundancy(Loc, MacroName, IfndefStack, DK_Ifdef, DK_Ifndef,
                         false);
  }

  void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
              const MacroDefinition &MacroDefinition) override {
    std::string MacroName = PP.getSpelling(MacroNameTok);
    CheckMacroRedundancy(Loc, MacroName, IfndefStack, DK_Ifndef, DK_Ifndef,
                         true);
    CheckMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifndef, DK_Ifdef,
                         false);
  }

  void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
    if (!IfStack.empty() && IfLoc == IfStack.back().Loc)
      IfStack.pop_back();
    if (!IfdefStack.empty() && IfLoc == IfdefStack.back().Loc)
      IfdefStack.pop_back();
    if (!IfndefStack.empty() && IfLoc == IfndefStack.back().Loc)
      IfndefStack.pop_back();
  }

private:
  void CheckMacroRedundancy(SourceLocation Loc, StringRef MacroName,
                            SmallVector<PreprocessorEntry, 4> &Stack,
                            DirectiveKind WarningKind, DirectiveKind NoteKind,
                            bool Store) {
    if (PP.getSourceManager().isInMainFile(Loc)) {
      for (const auto &Entry : Stack) {
        if (Entry.Condition == MacroName) {
          Check.diag(Loc, WarningDescription) << WarningKind;
          Check.diag(Entry.Loc, NoteDescription, DiagnosticIDs::Note)
              << NoteKind;
        }
      }
    }

    if (Store)
      // This is an actual directive to be remembered.
      Stack.push_back({Loc, MacroName});
  }

  ClangTidyCheck &Check;
  Preprocessor &PP;
  SmallVector<PreprocessorEntry, 4> IfStack;
  SmallVector<PreprocessorEntry, 4> IfdefStack;
  SmallVector<PreprocessorEntry, 4> IfndefStack;
  const std::string WarningDescription;
  const std::string NoteDescription;
};
} // namespace

void RedundantPreprocessorCheck::registerPPCallbacks(
    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
  PP->addPPCallbacks(
      ::std::make_unique<RedundantPreprocessorCallbacks>(*this, *PP));
}

} // namespace readability
} // namespace tidy
} // namespace clang