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].push_back(*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 @@ -311,9 +311,14 @@ if (auto Kind = kindForReference(R)) Builder.addToken(R.NameLoc, *Kind); }); - // Add highlightings for macro expansions. - for (const auto &M : AST.getMacros().Ranges) + // Add highlightings for macro references. + 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 @@ -895,6 +895,7 @@ } auto Loc = SM.getMacroArgExpandedLocation( getBeginningOfIdentifier(Pos, SM, AST.getASTContext().getLangOpts())); + // TODO: should we handle macros, too? // We also show references to the targets of using-decls, so we include // DeclRelation::Underlying. @@ -915,8 +916,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,97 @@ +#include "Annotations.h" +#include "CollectMacros.h" +#include "Matchers.h" +#include "SourceCode.h" +#include "TestTU.h" +#include "index/SymbolID.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/ScopedPrinter.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +using testing::UnorderedElementsAreArray; + +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", + // FIXME: Locating macro in duplicate definitions doesn't work. Enable + // this once LocateMacro is fixed. + // R"cpp(// Multiple definitions. + // #define $1[[a$1^bc]] 1 + // int func1() { int a = $1[[abc]];} + // #undef $1[[abc]] + + // #define $2[[a$2^bc]] 2 + // int func2() { int a = $2[[abc]];} + // #undef $2[[abc]] + // )cpp", + R"cpp( + #ifdef $Unknown[[UNDEFINED]] + #endif + )cpp", + R"cpp( + #ifndef $Unknown[[abc]] + #define $1[[abc]] + #ifdef $1[[abc]] + #endif + #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(); + auto ActualMacroRefs = AST.getMacros(); + auto &SM = AST.getSourceManager(); + auto &PP = AST.getPreprocessor(); + + // Known macros. + for (int I = 1;; I++) { + const auto ExpectedRefs = T.ranges(llvm::to_string(I)); + if (ExpectedRefs.empty()) + break; + + auto Loc = getBeginningOfIdentifier(ExpectedRefs.begin()->start, SM, + AST.getASTContext().getLangOpts()); + auto Macro = locateMacroAt(Loc, PP); + assert(Macro); + auto SID = getSymbolID(Macro->Name, Macro->Info, SM); + + EXPECT_THAT(ExpectedRefs, + UnorderedElementsAreArray(ActualMacroRefs.MacroRefs[*SID])) + << "Annotation=" << I << ", MacroName=" << Macro->Name + << ", Test = " << Test; + } + // Unkown macros. + EXPECT_THAT(AST.getMacros().UnknownMacros, + UnorderedElementsAreArray(T.ranges("Unknown"))) + << "Unknown macros doesn't match in " << 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()));