diff --git a/clang-tools-extra/include-cleaner/lib/Analysis.cpp b/clang-tools-extra/include-cleaner/lib/Analysis.cpp --- a/clang-tools-extra/include-cleaner/lib/Analysis.cpp +++ b/clang-tools-extra/include-cleaner/lib/Analysis.cpp @@ -40,6 +40,15 @@ tooling::stdlib::Recognizer Recognizer; for (auto *Root : ASTRoots) { walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) { + // If the symbol is spelled as a token paste, it spelling location points + // to file which is not a real file. We fallback to use + // the expansion location, this herustic is based on the assumption that + // most of token pastings are formed with the macro arguement, and it can + // allow us to detect uses of symbol defined by the common macro like + // `ABSL_FLAG`. + if (SM.isWrittenInScratchSpace(SM.getSpellingLoc(Loc))) + Loc = SM.getExpansionLoc(Loc); + auto FID = SM.getFileID(SM.getSpellingLoc(Loc)); if (FID != SM.getMainFileID() && FID != SM.getPreambleFileID()) return; diff --git a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp --- a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp +++ b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp @@ -519,6 +519,9 @@ const auto& SM = Ctx.getSourceManager(); for (Decl *Root : Roots) walkAST(*Root, [&](SourceLocation Loc, const NamedDecl &D, RefType T) { + if (SM.isWrittenInScratchSpace( SM.getSpellingLoc(Loc))) + Loc = SM.getExpansionLoc(Loc); + if(!SM.isWrittenInMainFile(SM.getSpellingLoc(Loc))) return; R.addRef(SymbolReference{D, Loc, T}); diff --git a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp --- a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp +++ b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp @@ -197,6 +197,31 @@ Pair(Code.point("4"), UnorderedElementsAre(MainFile)))); } +TEST_F(WalkUsedTest, TokenPasting) { + llvm::Annotations Code(R"cpp( + #define DEFINE_FLAG(name) int FLAG_##name = 0; + #define MY_FLAG(name) DEFINE_FLAG(MY_##name) + + #define PASTED_TOKEN X##2 + + $1^DEFINE_FLAG(abc); + $2^MY_FLAG(abc); + + int $3^TPASTED_TOKEN = 3; + )cpp"); + Inputs.Code = Code.code(); + + TestAST AST(Inputs); + auto &SM = AST.sourceManager(); + auto MainFile = Header(SM.getFileEntryForID(SM.getMainFileID())); + + EXPECT_THAT(offsetToProviders(AST, SM), + UnorderedElementsAre( + Pair(Code.point("1"), UnorderedElementsAre(MainFile)), + Pair(Code.point("2"), UnorderedElementsAre(MainFile)), + Pair(Code.point("3"), UnorderedElementsAre(MainFile)))); +} + class AnalyzeTest : public testing::Test { protected: TestInputs Inputs;