diff --git a/clang-tools-extra/clangd/ClangdUnit.h b/clang-tools-extra/clangd/ClangdUnit.h --- a/clang-tools-extra/clangd/ClangdUnit.h +++ b/clang-tools-extra/clangd/ClangdUnit.h @@ -116,6 +116,7 @@ const IncludeStructure &getIncludeStructure() const; const CanonicalIncludes &getCanonicalIncludes() const; + const std::vector &getMainFileMacroExpansionLocations() const; /// Tokens recorded while parsing the main file. /// (!) does not have tokens from the preamble. const syntax::TokenBuffer &getTokens() const { return Tokens; } @@ -124,6 +125,7 @@ ParsedAST(std::shared_ptr Preamble, std::unique_ptr Clang, std::unique_ptr Action, syntax::TokenBuffer Tokens, + std::vector MainFileMacroExpLocs, std::vector LocalTopLevelDecls, std::vector Diags, IncludeStructure Includes, CanonicalIncludes CanonIncludes); @@ -143,6 +145,9 @@ /// - Does not have spelled or expanded tokens for files from preamble. syntax::TokenBuffer Tokens; + /// The start locations of all macro expansions spelled inside the main file. + /// Does not include expansions from inside other macro expansions. + std::vector MainFileMacroExpLocs; // Data, stored after parsing. std::vector Diags; // Top-level decls inside the current file. Not that this does not include diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp --- a/clang-tools-extra/clangd/ClangdUnit.cpp +++ b/clang-tools-extra/clangd/ClangdUnit.cpp @@ -32,6 +32,7 @@ #include "clang/Index/IndexingAction.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/MacroInfo.h" +#include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/Sema.h" @@ -103,6 +104,24 @@ std::vector TopLevelDecls; }; +class CollectMainFileMacroExpansions : public PPCallbacks { + const SourceManager &SM; + std::vector &MainFileMacroLocs; + +public: + CollectMainFileMacroExpansions(const SourceManager &SM, + std::vector &MainFileMacroLocs) + : SM(SM), MainFileMacroLocs(MainFileMacroLocs) {} + + virtual void MacroExpands(const Token &MacroNameTok, + const MacroDefinition &MD, SourceRange Range, + const MacroArgs *Args) { + SourceLocation L = MacroNameTok.getLocation(); + if (isInsideMainFile(SM.getSpellingLoc(L), SM) && !L.isMacroID()) + MainFileMacroLocs.push_back(L); + } +}; + class CollectMainFileMacros : public PPCallbacks { public: explicit CollectMainFileMacros(const SourceManager &SM, @@ -414,6 +433,11 @@ // (We can't *just* use the replayed includes, they don't have Resolved path). Clang->getPreprocessor().addPPCallbacks( collectIncludeStructureCallback(Clang->getSourceManager(), &Includes)); + // Collect the macro expansions in the main file. + std::vector MainFileMacroExpLocs; + Clang->getPreprocessor().addPPCallbacks( + std::make_unique( + Clang->getSourceManager(), MainFileMacroExpLocs)); // Copy over the includes from the preamble, then combine with the // non-preamble includes below. @@ -462,7 +486,8 @@ if (Preamble) Diags.insert(Diags.begin(), Preamble->Diags.begin(), Preamble->Diags.end()); return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action), - std::move(Tokens), std::move(ParsedDecls), std::move(Diags), + std::move(Tokens), std::move(MainFileMacroExpLocs), + std::move(ParsedDecls), std::move(Diags), std::move(Includes), std::move(CanonIncludes)); } @@ -501,6 +526,11 @@ return LocalTopLevelDecls; } +const std::vector & +ParsedAST::getMainFileMacroExpansionLocations() const { + return MainFileMacroExpLocs; +} + const std::vector &ParsedAST::getDiagnostics() const { return Diags; } std::size_t ParsedAST::getUsedBytes() const { @@ -557,11 +587,13 @@ std::unique_ptr Clang, std::unique_ptr Action, syntax::TokenBuffer Tokens, + std::vector MainFileMacroExpLocs, std::vector LocalTopLevelDecls, std::vector Diags, IncludeStructure Includes, CanonicalIncludes CanonIncludes) : Preamble(std::move(Preamble)), Clang(std::move(Clang)), Action(std::move(Action)), Tokens(std::move(Tokens)), + MainFileMacroExpLocs(std::move(MainFileMacroExpLocs)), Diags(std::move(Diags)), LocalTopLevelDecls(std::move(LocalTopLevelDecls)), Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) { diff --git a/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp b/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdUnitTests.cpp @@ -244,6 +244,47 @@ EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "}"); } +TEST(ClangdUnitTest, CollectsMainFileMacroExpansions) { + Annotations TestCase(R"cpp( + #define MACRO_ARGS(X, Y) X Y + ^ID(int A); + // Macro arguments included. + ^MACRO_ARGS(^MACRO_ARGS(^MACRO_EXP(int), A), ^ID(= 2)); + + // Macro names inside other macros not included. + #define FOO BAR + #define BAR 1 + int A = ^FOO; + + // Macros from token concatenations included. + #define CONCAT(X) X##1() + #define MACRO1() 123 + int B = ^CONCAT(^MACRO); + + // Macros included not from preamble not included. + #include "foo.inc" + )cpp"); + auto TU = TestTU::withCode(TestCase.code()); + TU.HeaderCode = R"cpp( + #define ID(X) X + #define MACRO_EXP(X) ID(X) + MACRO_EXP(int B); + )cpp"; + TU.AdditionalFiles["foo.inc"] = R"cpp( + int C = ID(1); + #define DEF 1 + int D = DEF; + )cpp"; + ParsedAST AST = TU.build(); + const std::vector &MacroExpansionLocations = + AST.getMainFileMacroExpansionLocations(); + std::vector MacroExpansionPositions; + for (const auto &L : MacroExpansionLocations) + MacroExpansionPositions.push_back( + sourceLocToPosition(AST.getSourceManager(), L)); + EXPECT_EQ(MacroExpansionPositions, TestCase.points()); +} + } // namespace } // namespace clangd } // namespace clang