diff --git a/clang-tools-extra/clangd/CollectMacros.h b/clang-tools-extra/clangd/CollectMacros.h --- a/clang-tools-extra/clangd/CollectMacros.h +++ b/clang-tools-extra/clangd/CollectMacros.h @@ -9,10 +9,13 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_COLLECTEDMACROS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COLLECTEDMACROS_H +#include "AST.h" #include "Protocol.h" #include "SourceCode.h" +#include "index/SymbolID.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Lex/PPCallbacks.h" +#include "llvm/ADT/DenseMap.h" #include namespace clang { @@ -22,7 +25,11 @@ llvm::StringSet<> Names; // Instead of storing SourceLocation, we have to store the token range because // SourceManager from preamble is not available when we build the AST. - std::vector Ranges; + llvm::DenseMap> MacroRefs; + // Somtimes it is not possible to compute the SymbolID for the Macro, e.g. a + // reference to an undefined macro. Store them separately, e.g. for semantic + // highlighting. + std::vector UnknownMacros; }; /// Collects macro references (e.g. definitions, expansions) in the main file. @@ -81,7 +88,11 @@ if (auto Range = getTokenRange(SM, LangOpts, Loc)) { Out.Names.insert(MacroNameTok.getIdentifierInfo()->getName()); - Out.Ranges.push_back(*Range); + if (auto SID = + getSymbolID(MacroNameTok.getIdentifierInfo()->getName(), MI, SM)) + Out.MacroRefs[*SID].insert(*Range); + else + Out.UnknownMacros.push_back(*Range); } } const SourceManager &SM; diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -312,8 +312,13 @@ Builder.addToken(R.NameLoc, *Kind); }); // Add highlightings for macro expansions. - for (const auto &M : AST.getMacros().Ranges) + for (const auto &SIDToRefs : AST.getMacros().MacroRefs) { + for (const auto &M : SIDToRefs.second) + Builder.addToken({HighlightingKind::Macro, M}); + } + for (const auto &M : AST.getMacros().UnknownMacros) Builder.addToken({HighlightingKind::Macro, M}); + return std::move(Builder).collect(); } diff --git a/clang-tools-extra/clangd/XRefs.cpp b/clang-tools-extra/clangd/XRefs.cpp --- a/clang-tools-extra/clangd/XRefs.cpp +++ b/clang-tools-extra/clangd/XRefs.cpp @@ -21,6 +21,7 @@ #include "index/Merge.h" #include "index/Relation.h" #include "index/SymbolCollector.h" +#include "index/SymbolID.h" #include "index/SymbolLocation.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -47,6 +48,7 @@ #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" +#include namespace clang { namespace clangd { @@ -891,7 +893,28 @@ } auto Loc = SM.getMacroArgExpandedLocation( getBeginningOfIdentifier(Pos, SM, AST.getASTContext().getLangOpts())); - // TODO: should we handle macros, too? + + // Handle macros. + if (auto Macro = locateMacroAt(Loc, AST.getPreprocessor())) { + if (auto MacroSID = getSymbolID(Macro->Name, Macro->Info, SM)) { + const auto &IDToRefs = AST.getMacros().MacroRefs; + auto Refs = IDToRefs.find(*MacroSID); + if (Refs != IDToRefs.end()) { + auto URIMainFile = + URIForFile::canonicalize(*MainFilePath, *MainFilePath); + for (const auto Ref : Refs->second) { + Location Loc; + Loc.uri = URIMainFile; + Loc.range = Ref; + Results.push_back(Loc); + } + } + } + if (Results.size() > Limit) + Results.resize(Limit); + return Results; + } + // We also show references to the targets of using-decls, so we include // DeclRelation::Underlying. DeclRelationSet Relations = DeclRelation::TemplatePattern | @@ -911,8 +934,7 @@ MainFileRefs.end()); for (const auto &Ref : MainFileRefs) { if (auto Range = - getTokenRange(AST.getASTContext().getSourceManager(), - AST.getASTContext().getLangOpts(), Ref.Loc)) { + getTokenRange(SM, AST.getASTContext().getLangOpts(), Ref.Loc)) { Location Result; Result.range = *Range; Result.uri = URIForFile::canonicalize(*MainFilePath, *MainFilePath); diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -30,6 +30,7 @@ ClangdTests.cpp CodeCompleteTests.cpp CodeCompletionStringsTests.cpp + CollectMacrosTests.cpp ContextTests.cpp DexTests.cpp DiagnosticsTests.cpp diff --git a/clang-tools-extra/clangd/unittests/CollectMacrosTests.cpp b/clang-tools-extra/clangd/unittests/CollectMacrosTests.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/CollectMacrosTests.cpp @@ -0,0 +1,86 @@ +#include "Annotations.h" +#include "CollectMacros.h" +#include "Matchers.h" +#include "TestTU.h" +#include "llvm/Support/ScopedPrinter.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +using testing::UnorderedElementsAreArray; + +// Assumes that the ranges are named from 1 to N for some N. +Matcher>> +AreMacroRefsFrom(const Annotations &Test) { + std::vector>> Expected; + for (int I = 1;; I++) { + const auto Refs = Test.ranges(llvm::to_string(I)); + if (Refs.empty()) + break; + Expected.push_back(UnorderedElementsAreArray(Refs)); + } + return UnorderedElementsAreArray(Expected); +} + +std::vector> collectKnownReferences(MainFileMacros M) { + std::vector> Result; + for (const auto &SIDToRef : M.MacroRefs) { + Result.emplace_back(SIDToRef.second.begin(), SIDToRef.second.end()); + } + return Result; +} + +TEST(CollectMainFileMacros, SelectedMacros) { + // References of the same symbol must have the ranges with the same + // name(integer). If there are N different symbols then they must be named + // from 1 to N. Macros for which SymbolID cannot be computed must be named + // "Unknown". + const char *Tests[] = { + R"cpp(// Macros: Cursor on definition. + #define $1[[FOO]](x,y) (x + y) + int main() { int x = $1[[FOO]]($1[[FOO]](3, 4), $1[[FOO]](5, 6)); } + )cpp", + R"cpp(// Multiple definitions. + #define $1[[abc]] 1 + int func1() { int a = $1[[abc]];} + #undef $1[[abc]] + + #define $2[[abc]] 2 + int func2() { int a = $2[[abc]];} + #undef $2[[abc]] + )cpp", + R"cpp( + #ifdef $Unknown[[UNDEFINED]] + #endif + )cpp", + R"cpp( + // Macros from token concatenations not included. + #define $1[[CONCAT]](X) X##A() + #define $2[[PREPEND]](X) MACRO##X() + #define $3[[MACROA]]() 123 + int B = $1[[CONCAT]](MACRO); + int D = $2[[PREPEND]](A) + )cpp", + R"cpp( + // FIXME: Macro names in a definition are not detected. + #define $1[[MACRO_ARGS2]](X, Y) X Y + #define $2[[FOO]] BAR + #define $3[[BAR]] 1 + int A = $2[[FOO]]; + )cpp"}; + for (const char *Test : Tests) { + Annotations T(Test); + auto AST = TestTU::withCode(T.code()).build(); + EXPECT_THAT(AST.getMacros().UnknownMacros, + UnorderedElementsAreArray(T.ranges("Unknown"))) + << Test; + EXPECT_THAT(collectKnownReferences(AST.getMacros()), AreMacroRefsFrom(T)) + << Test; + } +} +} // namespace +} // namespace clangd +} // namespace clang \ No newline at end of file diff --git a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp --- a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp @@ -260,6 +260,15 @@ // Includes macro expansions in arguments that are expressions ^assert(0 <= ^BAR); } + + #ifdef ^UNDEFINED + #endif + + #define ^MULTIPLE_DEFINITION 1 + #undef ^MULTIPLE_DEFINITION + + #define ^MULTIPLE_DEFINITION 2 + #undef ^MULTIPLE_DEFINITION )cpp"); auto TU = TestTU::withCode(TestCase.code()); TU.HeaderCode = R"cpp( @@ -274,7 +283,11 @@ )cpp"; ParsedAST AST = TU.build(); std::vector MacroExpansionPositions; - for (const auto &R : AST.getMacros().Ranges) + for (const auto &SIDToRefs : AST.getMacros().MacroRefs) { + for (const auto &R : SIDToRefs.second) + MacroExpansionPositions.push_back(R.start); + } + for (const auto &R : AST.getMacros().UnknownMacros) MacroExpansionPositions.push_back(R.start); EXPECT_THAT(MacroExpansionPositions, testing::UnorderedElementsAreArray(TestCase.points())); diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -2050,6 +2050,32 @@ } // namespace ns int main() { [[^ns]]::Foo foo; } )cpp", + + R"cpp(// Macros + #define [[FOO]](x,y) (x + y) + // FIXME: No references for nested macros. + #define BAR(x,y,z) (FOO(x, y) * FOO(y, z)) + int main() { + int x = [[FOO]](1, 2); + int y = [[FOO]]([[FO^O]](x, 1), [[FOO]](1, 1)); + int z = BAR(1, 2, 3); + } + #define FOO(x) (x + 1) + )cpp", + + R"cpp(// Macros: Cursor on definition. + #define [[F^OO]](x,y) (x + y) + int main() { int x = [[FOO]](1, 2); } + )cpp", + + R"cpp( // Choose correct definiton in case of multiple definitions. + #define abc 1 + #undef abc + + #define [[abc]] 2 + int main() { int a = [[abc]];} + #undef [[ab^c]] + )cpp", }; for (const char *Test : Tests) { Annotations T(Test);