clang-tools 23.0.0git
RedundantPreprocessorCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
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
16
17static StringRef getConditionText(SourceLocation Loc, const SourceManager &SM,
18 const LangOptions &LangOpts) {
19 bool Invalid = false;
20 const FileID FID = SM.getFileID(Loc);
21 const StringRef Buffer = SM.getBufferData(FID, &Invalid);
22 if (Invalid)
23 return {};
24
25 // Initialize a raw lexer starting exactly at the condition's location
26 Lexer RawLexer(SM.getLocForStartOfFile(FID), LangOpts, Buffer.begin(),
27 SM.getCharacterData(Loc), Buffer.end());
28 RawLexer.SetCommentRetentionState(true);
29
30 Token Tok;
31 // Lex the 'if' token itself
32 RawLexer.LexFromRawLexer(Tok);
33
34 const unsigned StartOffset = SM.getFileOffset(Tok.getEndLoc());
35 unsigned EndOffset = StartOffset;
36
37 // Lex tokens until we hit the start of a new line or EOF.
38 // The lexer handles backslash line continuations automatically.
39 while (!RawLexer.LexFromRawLexer(Tok)) {
40 if (Tok.isAtStartOfLine() || Tok.is(tok::eof))
41 break;
42 EndOffset = SM.getFileOffset(Tok.getLocation()) + Tok.getLength();
43 }
44
45 if (EndOffset <= StartOffset)
46 return {};
47
48 // Extract the raw text from the buffer to preserve original spacing
49 return Buffer.substr(StartOffset, EndOffset - StartOffset).trim();
50}
51
52namespace {
53
54/// Information about an opening preprocessor directive.
55struct PreprocessorEntry {
56 SourceLocation Loc;
57 /// Condition used after the preprocessor directive.
58 std::string Condition;
59};
60
61const char WarningDescription[] =
62 "nested redundant %select{#if|#ifdef|#ifndef}0; consider removing it";
63const char NoteDescription[] = "previous %select{#if|#ifdef|#ifndef}0 was here";
64
65class RedundantPreprocessorCallbacks : public PPCallbacks {
66 enum DirectiveKind { DK_If = 0, DK_Ifdef = 1, DK_Ifndef = 2 };
67
68public:
69 explicit RedundantPreprocessorCallbacks(ClangTidyCheck &Check,
70 Preprocessor &PP)
71 : Check(Check), PP(PP) {}
72
73 void If(SourceLocation Loc, SourceRange ConditionRange,
74 ConditionValueKind ConditionValue) override {
75 const StringRef Condition =
76 getConditionText(Loc, PP.getSourceManager(), PP.getLangOpts());
77 checkMacroRedundancy(Loc, Condition, IfStack, DK_If, DK_If, true);
78 }
79
80 void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
81 const MacroDefinition &MacroDefinition) override {
82 const std::string MacroName = PP.getSpelling(MacroNameTok);
83 checkMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifdef, DK_Ifdef, true);
84 checkMacroRedundancy(Loc, MacroName, IfndefStack, DK_Ifdef, DK_Ifndef,
85 false);
86 }
87
88 void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
89 const MacroDefinition &MacroDefinition) override {
90 const std::string MacroName = PP.getSpelling(MacroNameTok);
91 checkMacroRedundancy(Loc, MacroName, IfndefStack, DK_Ifndef, DK_Ifndef,
92 true);
93 checkMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifndef, DK_Ifdef,
94 false);
95 }
96
97 void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
98 if (!IfStack.empty() && IfLoc == IfStack.back().Loc)
99 IfStack.pop_back();
100 if (!IfdefStack.empty() && IfLoc == IfdefStack.back().Loc)
101 IfdefStack.pop_back();
102 if (!IfndefStack.empty() && IfLoc == IfndefStack.back().Loc)
103 IfndefStack.pop_back();
104 }
105
106private:
107 void checkMacroRedundancy(SourceLocation Loc, StringRef MacroName,
108 SmallVector<PreprocessorEntry, 4> &Stack,
109 DirectiveKind WarningKind, DirectiveKind NoteKind,
110 bool Store) {
111 if (PP.getSourceManager().isInMainFile(Loc)) {
112 for (const auto &Entry : Stack) {
113 if (Entry.Condition == MacroName) {
114 Check.diag(Loc, WarningDescription) << WarningKind;
115 Check.diag(Entry.Loc, NoteDescription, DiagnosticIDs::Note)
116 << NoteKind;
117 }
118 }
119 }
120
121 if (Store)
122 // This is an actual directive to be remembered.
123 Stack.push_back({Loc, std::string(MacroName)});
124 }
125
126 ClangTidyCheck &Check;
127 Preprocessor &PP;
128 SmallVector<PreprocessorEntry, 4> IfStack;
129 SmallVector<PreprocessorEntry, 4> IfdefStack;
130 SmallVector<PreprocessorEntry, 4> IfndefStack;
131};
132} // namespace
133
135 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
136 PP->addPPCallbacks(
137 ::std::make_unique<RedundantPreprocessorCallbacks>(*this, *PP));
138}
139
140} // namespace clang::tidy::readability
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
static StringRef getConditionText(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)