diff --git a/clang-tools-extra/include-cleaner/lib/Record.cpp b/clang-tools-extra/include-cleaner/lib/Record.cpp --- a/clang-tools-extra/include-cleaner/lib/Record.cpp +++ b/clang-tools-extra/include-cleaner/lib/Record.cpp @@ -83,14 +83,54 @@ recordMacroRef(MacroName, *MI); } + void Ifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + if (!Active) + return; + if (const auto *MI = MD.getMacroInfo()) + recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous); + } + + void Ifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + if (!Active) + return; + if (const auto *MI = MD.getMacroInfo()) + recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous); + } + + void Elifdef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + if (!Active) + return; + if (const auto *MI = MD.getMacroInfo()) + recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous); + } + + void Elifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDefinition &MD) override { + if (!Active) + return; + if (const auto *MI = MD.getMacroInfo()) + recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous); + } + + void Defined(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range) override { + if (!Active) + return; + if (const auto *MI = MD.getMacroInfo()) + recordMacroRef(MacroNameTok, *MI, RefType::Ambiguous); + } + private: - void recordMacroRef(const Token &Tok, const MacroInfo &MI) { + void recordMacroRef(const Token &Tok, const MacroInfo &MI, + RefType RT = RefType::Explicit) { if (MI.isBuiltinMacro()) return; // __FILE__ is not a reference. - Recorded.MacroReferences.push_back( - SymbolReference{Tok.getLocation(), - Macro{Tok.getIdentifierInfo(), MI.getDefinitionLoc()}, - RefType::Explicit}); + Recorded.MacroReferences.push_back(SymbolReference{ + Tok.getLocation(), + Macro{Tok.getIdentifierInfo(), MI.getDefinitionLoc()}, RT}); } bool Active = false; diff --git a/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/RecordTest.cpp @@ -7,12 +7,13 @@ //===----------------------------------------------------------------------===// #include "clang-include-cleaner/Record.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Testing/TestAST.h" #include "clang/Tooling/Inclusions/StandardLibrary.h" -#include "llvm/Support/VirtualFileSystem.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Testing/Support/Annotations.h" #include "gmock/gmock.h" @@ -218,6 +219,71 @@ EXPECT_THAT(ExpOffsets, ElementsAreArray(MainFile.points("exp"))); } +TEST_F(RecordPPTest, CapturesConditionalMacroRefs) { + llvm::Annotations Header(R"cpp( + #define Z 2 + #define CPU + )cpp"); + + llvm::Annotations MainFile(R"cpp( + #include "header.h" + + #define X 1 + #define RAM + + #ifdef ^X + #define Y 2 + #endif + + #if defined(^X) + #define Q 3 + #endif + + #ifndef ^Z + #define W 4 + #endif + + int main() { + int result; + #ifdef GPU + result = 1; + #elifdef ^RAM + result = 2; + #else + result = 3; + #endif + + int result2; + #ifndef ^X + result2 = 1; + #elifndef ^CPU + result2 = 2; + #else + result2 = 3; + #endif + return 0; + } + )cpp"); + + Inputs.Code = MainFile.code(); + Inputs.ExtraFiles["header.h"] = Header.code(); + auto AST = build(); + + std::vector<unsigned> RefOffsets; + std::vector<std::string> RefNames = {"X", "X", "Z", "RAM", "X", "CPU"}; + SourceManager &SM = AST.sourceManager(); + ASSERT_THAT(Recorded.MacroReferences, Not(IsEmpty())); + for (auto I = 0u; I < Recorded.MacroReferences.size(); I++) { + const auto &Ref = Recorded.MacroReferences[I]; + auto [FID, Off] = SM.getDecomposedLoc(Ref.RefLocation); + ASSERT_EQ(FID, SM.getMainFileID()); + EXPECT_EQ(Ref.RT, RefType::Ambiguous); + EXPECT_EQ(RefNames[I], Ref.Target.macro().Name->getName()); + RefOffsets.push_back(Off); + } + EXPECT_THAT(RefOffsets, ElementsAreArray(MainFile.points())); +} + // Matches an Include* on the specified line; MATCHER_P(line, N, "") { return arg->Line == (unsigned)N; }