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