Index: clang-tools-extra/trunk/clangd/ClangdUnit.h =================================================================== --- clang-tools-extra/trunk/clangd/ClangdUnit.h +++ clang-tools-extra/trunk/clangd/ClangdUnit.h @@ -118,6 +118,9 @@ const IncludeStructure &getIncludeStructure() const; const CanonicalIncludes &getCanonicalIncludes() const; + /// The start locations of all macro expansions spelled inside the main file. + /// Does not include expansions from inside other macro expansions. + llvm::ArrayRef getMainFileExpansions() const; /// Tokens recorded while parsing the main file. /// (!) does not have tokens from the preamble. const syntax::TokenBuffer &getTokens() const { return Tokens; } @@ -126,6 +129,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); @@ -145,6 +149,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 Index: clang-tools-extra/trunk/clangd/ClangdUnit.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ClangdUnit.cpp +++ clang-tools-extra/trunk/clangd/ClangdUnit.cpp @@ -22,6 +22,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TokenKinds.h" #include "clang/Frontend/CompilerInstance.h" @@ -32,6 +33,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 +105,28 @@ std::vector TopLevelDecls; }; +// CollectMainFileMacroExpansions and CollectMainFileMacros are two different +// classes as CollectMainFileMacroExpansions is only used when building the AST +// for the main file. CollectMainFileMacros is only used when building the +// preamble. +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 (!L.isMacroID() && isInsideMainFile(L, SM)) + MainFileMacroLocs.push_back(L); + } +}; + class CollectMainFileMacros : public PPCallbacks { public: explicit CollectMainFileMacros(const SourceManager &SM, @@ -417,6 +441,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. @@ -470,7 +499,8 @@ Diags.insert(Diags.end(), D.begin(), D.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)); } @@ -509,6 +539,10 @@ return LocalTopLevelDecls; } +llvm::ArrayRef ParsedAST::getMainFileExpansions() const { + return MainFileMacroExpLocs; +} + const std::vector &ParsedAST::getDiagnostics() const { return Diags; } std::size_t ParsedAST::getUsedBytes() const { @@ -565,11 +599,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)) { Index: clang-tools-extra/trunk/clangd/unittests/ClangdUnitTests.cpp =================================================================== --- clang-tools-extra/trunk/clangd/unittests/ClangdUnitTests.cpp +++ clang-tools-extra/trunk/clangd/unittests/ClangdUnitTests.cpp @@ -262,6 +262,56 @@ EXPECT_NE(buildCompilerInvocation(Inputs, IgnoreDiags), nullptr); } +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 not included. + #define CONCAT(X) X##A() + #define PREPEND(X) MACRO##X() + #define MACROA() 123 + int B = ^CONCAT(MACRO); + int D = ^PREPEND(A) + + // Macros included not from preamble not included. + #include "foo.inc" + + #define assert(COND) if (!(COND)) { printf("%s", #COND); exit(0); } + + void test() { + // Includes macro expansions in arguments that are expressions + ^assert(0 <= ^BAR); + } + )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.getMainFileExpansions(); + 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