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 @@ -53,7 +53,7 @@ SourceRange Range, const MacroArgs *Args) override { if (!Active) return; - recordMacroRef(MacroName, *MD.getMacroInfo()); + recordMacroRef(MacroName, *MD.getMacroInfo(), RefType::Explicit); } void MacroDefined(const Token &MacroName, const MacroDirective *MD) override { @@ -71,7 +71,7 @@ llvm::is_contained(MI->params(), II)) continue; if (const MacroInfo *MI = PP.getMacroInfo(II)) - recordMacroRef(Tok, *MI); + recordMacroRef(Tok, *MI, RefType::Explicit); } } @@ -80,17 +80,33 @@ if (!Active) return; if (const auto *MI = MD.getMacroInfo()) - recordMacroRef(MacroName, *MI); + recordMacroRef(MacroName, *MI, RefType::Explicit); + } + + 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); } private: - void recordMacroRef(const Token &Tok, const MacroInfo &MI) { + void recordMacroRef(const Token &Tok, const MacroInfo &MI, RefType RT) { if (MI.isBuiltinMacro()) return; // __FILE__ is not a reference. Recorded.MacroReferences.push_back( SymbolReference{Tok.getLocation(), Macro{Tok.getIdentifierInfo(), MI.getDefinitionLoc()}, - RefType::Explicit}); + 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,72 @@ EXPECT_THAT(ExpOffsets, ElementsAreArray(MainFile.points("exp"))); } +TEST_F(RecordPPTest, CapturesConditionalMacroRefs) { + llvm::Annotations Header(R"cpp( + #define Z 2 + )cpp"); + + llvm::Annotations MainFile(R"cpp( + #include "header.h" + + #define X 1 + + #ifdef ^X + #define Y 2 + #endif + + #ifndef ^Z + #define W 3 + #endif + )cpp"); + + Inputs.Code = MainFile.code(); + Inputs.ExtraFiles["header.h"] = Header.code(); + auto AST = build(); + + std::vector RefOffsets; + std::vector RefNames = {"X", "Z"}; + 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())); +} + +TEST_F(RecordPPTest, CapturesIfNDefMacroRefs) { + llvm::Annotations Header(R"cpp( + #define Z 2 + )cpp"); + + llvm::Annotations MainFile(R"cpp( + #include "header.h" + + #ifndef $def^Z + #define W 3 + #endif + )cpp"); + + Inputs.Code = MainFile.code(); + Inputs.ExtraFiles["header.h"] = Header.code(); + auto AST = build(); + ASSERT_THAT(Recorded.MacroReferences, Not(IsEmpty())); + + SourceManager &SM = AST.sourceManager(); + SourceLocation Def = SM.getComposedLoc(SM.getMainFileID(), MainFile.point("def")); + + SymbolReference XRef = Recorded.MacroReferences.front(); + EXPECT_EQ(XRef.RT, RefType::Ambiguous); + EXPECT_EQ("Z", XRef.Target.macro().Name->getName()); + EXPECT_EQ(XRef.RefLocation, Def); +} + // Matches an Include* on the specified line; MATCHER_P(line, N, "") { return arg->Line == (unsigned)N; }