diff --git a/clang-tools-extra/clangd/SourceCode.h b/clang-tools-extra/clangd/SourceCode.h --- a/clang-tools-extra/clangd/SourceCode.h +++ b/clang-tools-extra/clangd/SourceCode.h @@ -232,6 +232,11 @@ llvm::StringMap collectIdentifiers(llvm::StringRef Content, const format::FormatStyle &Style); +/// Collects all ranges of the given identifier in the source code. +std::vector collectIdentifierRanges(llvm::StringRef Identifier, + llvm::StringRef Content, + const LangOptions &LangOpts); + /// Collects words from the source code. /// Unlike collectIdentifiers: /// - also finds text in comments: diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp --- a/clang-tools-extra/clangd/SourceCode.cpp +++ b/clang-tools-extra/clangd/SourceCode.cpp @@ -719,41 +719,53 @@ return formatReplacements(Code, std::move(*CleanReplaces), Style); } -void lex(llvm::StringRef Code, const format::FormatStyle &Style, - llvm::function_ref Action) { +void lex(llvm::StringRef Code, const LangOptions &LangOpts, + llvm::function_ref + Action) { // FIXME: InMemoryFileAdapter crashes unless the buffer is null terminated! std::string NullTerminatedCode = Code.str(); SourceManagerForFile FileSM("dummy.cpp", NullTerminatedCode); auto &SM = FileSM.get(); auto FID = SM.getMainFileID(); - Lexer Lex(FID, SM.getBuffer(FID), SM, format::getFormattingLangOpts(Style)); + // Create a raw lexer (with no associated preprocessor object). + Lexer Lex(FID, SM.getBuffer(FID), SM, LangOpts); Token Tok; while (!Lex.LexFromRawLexer(Tok)) - Action(Tok, sourceLocToPosition(SM, Tok.getLocation())); + Action(Tok, SM); // LexFromRawLexer returns true after it lexes last token, so we still have // one more token to report. - Action(Tok, sourceLocToPosition(SM, Tok.getLocation())); + Action(Tok, SM); } llvm::StringMap collectIdentifiers(llvm::StringRef Content, const format::FormatStyle &Style) { llvm::StringMap Identifiers; - lex(Content, Style, [&](const clang::Token &Tok, Position) { - switch (Tok.getKind()) { - case tok::identifier: - ++Identifiers[Tok.getIdentifierInfo()->getName()]; - break; - case tok::raw_identifier: + auto LangOpt = format::getFormattingLangOpts(Style); + lex(Content, LangOpt, [&](const clang::Token &Tok, const SourceManager &) { + if (Tok.getKind() == tok::raw_identifier) ++Identifiers[Tok.getRawIdentifier()]; - break; - default: - break; - } }); return Identifiers; } +std::vector collectIdentifierRanges(llvm::StringRef Identifier, + llvm::StringRef Content, + const LangOptions &LangOpts) { + std::vector Ranges; + lex(Content, LangOpts, [&](const clang::Token &Tok, const SourceManager &SM) { + if (Tok.getKind() != tok::raw_identifier) + return; + if (Tok.getRawIdentifier() != Identifier) + return; + auto Range = getTokenRange(SM, LangOpts, Tok.getLocation()); + if (!Range) + return; + Ranges.push_back(*Range); + }); + return Ranges; +} + namespace { struct NamespaceEvent { enum { @@ -786,8 +798,9 @@ std::string NSName; NamespaceEvent Event; - lex(Code, Style, [&](const clang::Token &Tok, Position P) { - Event.Pos = std::move(P); + lex(Code, format::getFormattingLangOpts(Style), + [&](const clang::Token &Tok,const SourceManager &SM) { + Event.Pos = sourceLocToPosition(SM, Tok.getLocation()); switch (Tok.getKind()) { case tok::raw_identifier: // In raw mode, this could be a keyword or a name. diff --git a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp --- a/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp +++ b/clang-tools-extra/clangd/unittests/SourceCodeTests.cpp @@ -680,6 +680,27 @@ EXPECT_EQ(Res.EnclosingNamespace, Case.EnclosingNamespace) << Test.code(); } } + +TEST(SourceCodeTests, IdentifierRanges) { + Annotations Code(R"cpp( + class [[Foo]] {}; + // Foo + /* Foo */ + void f([[Foo]]* foo1) { + [[Foo]] foo2; + auto S = [[Foo]](); +// cross-line identifier is not supported. +F\ +o\ +o foo2; + } + )cpp"); + LangOptions LangOpts; + LangOpts.CPlusPlus = true; + EXPECT_EQ(Code.ranges(), + collectIdentifierRanges("Foo", Code.code(), LangOpts)); +} + } // namespace } // namespace clangd } // namespace clang