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