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 | |