10#include "clang/Frontend/CompilerInstance.h"
11#include "clang/Lex/Lexer.h"
12#include "clang/Lex/PPCallbacks.h"
13#include "clang/Lex/Preprocessor.h"
18 const LangOptions &LangOpts) {
20 const FileID FID = SM.getFileID(Loc);
21 const StringRef Buffer = SM.getBufferData(FID, &Invalid);
26 Lexer RawLexer(SM.getLocForStartOfFile(FID), LangOpts, Buffer.begin(),
27 SM.getCharacterData(Loc), Buffer.end());
28 RawLexer.SetCommentRetentionState(
true);
32 RawLexer.LexFromRawLexer(Tok);
34 const unsigned StartOffset = SM.getFileOffset(Tok.getEndLoc());
35 unsigned EndOffset = StartOffset;
39 while (!RawLexer.LexFromRawLexer(Tok)) {
40 if (Tok.isAtStartOfLine() || Tok.is(tok::eof))
42 EndOffset = SM.getFileOffset(Tok.getLocation()) + Tok.getLength();
45 if (EndOffset <= StartOffset)
49 return Buffer.substr(StartOffset, EndOffset - StartOffset).trim();
55struct PreprocessorEntry {
58 std::string Condition;
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";
65class RedundantPreprocessorCallbacks :
public PPCallbacks {
66 enum DirectiveKind { DK_If = 0, DK_Ifdef = 1, DK_Ifndef = 2 };
71 : Check(Check), PP(PP) {}
73 void If(SourceLocation Loc, SourceRange ConditionRange,
74 ConditionValueKind ConditionValue)
override {
75 const StringRef Condition =
77 checkMacroRedundancy(Loc, Condition, IfStack, DK_If, DK_If,
true);
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,
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,
93 checkMacroRedundancy(Loc, MacroName, IfdefStack, DK_Ifndef, DK_Ifdef,
97 void Endif(SourceLocation Loc, SourceLocation IfLoc)
override {
98 if (!IfStack.empty() && IfLoc == IfStack.back().Loc)
100 if (!IfdefStack.empty() && IfLoc == IfdefStack.back().Loc)
101 IfdefStack.pop_back();
102 if (!IfndefStack.empty() && IfLoc == IfndefStack.back().Loc)
103 IfndefStack.pop_back();
107 void checkMacroRedundancy(SourceLocation Loc, StringRef MacroName,
108 SmallVector<PreprocessorEntry, 4> &Stack,
109 DirectiveKind WarningKind, DirectiveKind NoteKind,
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)
123 Stack.push_back({Loc, std::string(MacroName)});
126 ClangTidyCheck &Check;
128 SmallVector<PreprocessorEntry, 4> IfStack;
129 SmallVector<PreprocessorEntry, 4> IfdefStack;
130 SmallVector<PreprocessorEntry, 4> IfndefStack;
135 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
137 ::std::make_unique<RedundantPreprocessorCallbacks>(*
this, *PP));
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
static StringRef getConditionText(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)