1 //===--- RedundantPreprocessorCheck.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 "RedundantPreprocessorCheck.h" 10 #include "clang/Frontend/CompilerInstance.h" 11 12 namespace clang { 13 namespace tidy { 14 namespace readability { 15 16 namespace { 17 /// Information about an opening preprocessor directive. 18 struct PreprocessorEntry { 19 SourceLocation Loc; 20 /// Condition used after the preprocessor directive. 21 std::string Condition; 22 }; 23 24 class RedundantPreprocessorCallbacks : public PPCallbacks { 25 enum DirectiveKind { DK_If = 0, DK_Ifdef = 1, DK_Ifndef = 2 }; 26 27 public: 28 explicit RedundantPreprocessorCallbacks(ClangTidyCheck &Check, 29 Preprocessor &PP) 30 : Check(Check), PP(PP), 31 WarningDescription("nested redundant %select{#if|#ifdef|#ifndef}0; " 32 "consider removing it"), 33 NoteDescription("previous %select{#if|#ifdef|#ifndef}0 was here") {} 34 35 void If(SourceLocation Loc, SourceRange ConditionRange, 36 ConditionValueKind ConditionValue) override { 37 StringRef Condition = 38 Lexer::getSourceText(CharSourceRange::getTokenRange(ConditionRange), 39 PP.getSourceManager(), PP.getLangOpts()); 40 CheckMacroRedundancy(Loc, Condition, IfStack, DK_If, DK_If, true); 41 } 42 43 void Ifdef(SourceLocation Loc, const Token &MacroNameTok, 44 const MacroDefinition &MacroDefinition) override { 45 std::string MacroName = PP.getSpelling(MacroNameTok); 46 CheckMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifdef, DK_Ifdef, true); 47 CheckMacroRedundancy(Loc, MacroName, IfndefStack, DK_Ifdef, DK_Ifndef, 48 false); 49 } 50 51 void Ifndef(SourceLocation Loc, const Token &MacroNameTok, 52 const MacroDefinition &MacroDefinition) override { 53 std::string MacroName = PP.getSpelling(MacroNameTok); 54 CheckMacroRedundancy(Loc, MacroName, IfndefStack, DK_Ifndef, DK_Ifndef, 55 true); 56 CheckMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifndef, DK_Ifdef, 57 false); 58 } 59 60 void Endif(SourceLocation Loc, SourceLocation IfLoc) override { 61 if (!IfStack.empty() && IfLoc == IfStack.back().Loc) 62 IfStack.pop_back(); 63 if (!IfdefStack.empty() && IfLoc == IfdefStack.back().Loc) 64 IfdefStack.pop_back(); 65 if (!IfndefStack.empty() && IfLoc == IfndefStack.back().Loc) 66 IfndefStack.pop_back(); 67 } 68 69 private: 70 void CheckMacroRedundancy(SourceLocation Loc, StringRef MacroName, 71 SmallVector<PreprocessorEntry, 4> &Stack, 72 DirectiveKind WarningKind, DirectiveKind NoteKind, 73 bool Store) { 74 if (PP.getSourceManager().isInMainFile(Loc)) { 75 for (const auto &Entry : Stack) { 76 if (Entry.Condition == MacroName) { 77 Check.diag(Loc, WarningDescription) << WarningKind; 78 Check.diag(Entry.Loc, NoteDescription, DiagnosticIDs::Note) 79 << NoteKind; 80 } 81 } 82 } 83 84 if (Store) 85 // This is an actual directive to be remembered. 86 Stack.push_back({Loc, MacroName}); 87 } 88 89 ClangTidyCheck &Check; 90 Preprocessor &PP; 91 SmallVector<PreprocessorEntry, 4> IfStack; 92 SmallVector<PreprocessorEntry, 4> IfdefStack; 93 SmallVector<PreprocessorEntry, 4> IfndefStack; 94 const std::string WarningDescription; 95 const std::string NoteDescription; 96 }; 97 } // namespace 98 99 void RedundantPreprocessorCheck::registerPPCallbacks( 100 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { 101 PP->addPPCallbacks( 102 ::std::make_unique<RedundantPreprocessorCallbacks>(*this, *PP)); 103 } 104 105 } // namespace readability 106 } // namespace tidy 107 } // namespace clang 108