summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang-tools-extra/clangd/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clangd/ClangdLSPServer.cpp24
-rw-r--r--clang-tools-extra/clangd/ClangdServer.cpp14
-rw-r--r--clang-tools-extra/clangd/ClangdServer.h2
-rw-r--r--clang-tools-extra/clangd/ClangdUnit.cpp150
-rw-r--r--clang-tools-extra/clangd/ClangdUnit.h4
-rw-r--r--clang-tools-extra/clangd/Protocol.cpp10
-rw-r--r--clang-tools-extra/clangd/Protocol.h32
-rw-r--r--clang-tools-extra/clangd/ProtocolHandlers.cpp20
-rw-r--r--clang-tools-extra/clangd/ProtocolHandlers.h2
-rw-r--r--clang-tools-extra/test/clangd/definitions.test168
-rw-r--r--clang-tools-extra/test/clangd/formatting.test5
12 files changed, 428 insertions, 4 deletions
diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt
index f1c7098faf2..f5e70eed7b1 100644
--- a/clang-tools-extra/clangd/CMakeLists.txt
+++ b/clang-tools-extra/clangd/CMakeLists.txt
@@ -18,6 +18,7 @@ add_clang_library(clangDaemon
clangBasic
clangFormat
clangFrontend
+ clangIndex
clangSema
clangTooling
clangToolingCore
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index fde6b6fc4b3..1fd0e95aef4 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -69,6 +69,8 @@ public:
JSONOutput &Out) override;
void onCompletion(TextDocumentPositionParams Params, StringRef ID,
JSONOutput &Out) override;
+ void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
+ JSONOutput &Out) override;
private:
ClangdLSPServer &LangServer;
@@ -84,7 +86,8 @@ void ClangdLSPServer::LSPProtocolCallbacks::onInitialize(StringRef ID,
"documentRangeFormattingProvider": true,
"documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
"codeActionProvider": true,
- "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}
+ "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},
+ "definitionProvider": true
}}})");
}
@@ -191,6 +194,25 @@ void ClangdLSPServer::LSPProtocolCallbacks::onCompletion(
R"(,"result":[)" + Completions + R"(]})");
}
+void ClangdLSPServer::LSPProtocolCallbacks::onGoToDefinition(
+ TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) {
+
+ auto Items = LangServer.Server.findDefinitions(
+ Params.textDocument.uri.file,
+ Position{Params.position.line, Params.position.character}).Value;
+
+ std::string Locations;
+ for (const auto &Item : Items) {
+ Locations += Location::unparse(Item);
+ Locations += ",";
+ }
+ if (!Locations.empty())
+ Locations.pop_back();
+ Out.writeMessage(
+ R"({"jsonrpc":"2.0","id":)" + ID.str() +
+ R"(,"result":[)" + Locations + R"(]})");
+}
+
ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously)
: Out(Out), DiagConsumer(*this),
Server(CDB, DiagConsumer, FSProvider, RunSynchronously) {}
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 490780a42f3..5e668846229 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -271,3 +271,17 @@ std::string ClangdServer::dumpAST(PathRef File) {
});
return DumpFuture.get();
}
+
+Tagged<std::vector<Location>>
+ClangdServer::findDefinitions(PathRef File, Position Pos) {
+ auto FileContents = DraftMgr.getDraft(File);
+ assert(FileContents.Draft && "findDefinitions is called for non-added document");
+
+ std::vector<Location> Result;
+ auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+ Units.runOnUnit(File, *FileContents.Draft, ResourceDir, CDB, PCHs,
+ TaggedFS.Value, [&](ClangdUnit &Unit) {
+ Result = Unit.findDefinitions(Pos);
+ });
+ return make_tagged(std::move(Result), TaggedFS.Tag);
+}
diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h
index a8eba812540..8a2936f02df 100644
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ b/clang-tools-extra/clangd/ClangdServer.h
@@ -179,6 +179,8 @@ public:
Tagged<std::vector<CompletionItem>>
codeComplete(PathRef File, Position Pos,
llvm::Optional<StringRef> OverridenContents = llvm::None);
+ /// Get definition of symbol at a specified \p Line and \p Column in \p File.
+ Tagged<std::vector<Location>> findDefinitions(PathRef File, Position Pos);
/// Run formatting for \p Rng inside \p File.
std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
diff --git a/clang-tools-extra/clangd/ClangdUnit.cpp b/clang-tools-extra/clangd/ClangdUnit.cpp
index b1e0d4955d7..61bfe0bc25f 100644
--- a/clang-tools-extra/clangd/ClangdUnit.cpp
+++ b/clang-tools-extra/clangd/ClangdUnit.cpp
@@ -8,13 +8,21 @@
//===---------------------------------------------------------------------===//
#include "ClangdUnit.h"
+
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/Utils.h"
+#include "clang/Index/IndexingAction.h"
+#include "clang/Index/IndexDataConsumer.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/Support/Format.h"
+#include <algorithm>
+
using namespace clang::clangd;
using namespace clang;
@@ -259,3 +267,145 @@ std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
}
+
+namespace {
+/// Finds declarations locations that a given source location refers to.
+class DeclarationLocationsFinder : public index::IndexDataConsumer {
+ std::vector<Location> DeclarationLocations;
+ const SourceLocation &SearchedLocation;
+ ASTUnit &Unit;
+public:
+ DeclarationLocationsFinder(raw_ostream &OS,
+ const SourceLocation &SearchedLocation, ASTUnit &Unit) :
+ SearchedLocation(SearchedLocation), Unit(Unit) {
+ }
+
+ std::vector<Location> takeLocations() {
+ // Don't keep the same location multiple times.
+ // This can happen when nodes in the AST are visited twice.
+ std::sort(DeclarationLocations.begin(), DeclarationLocations.end());
+ auto last = std::unique(DeclarationLocations.begin(), DeclarationLocations.end());
+ DeclarationLocations.erase(last, DeclarationLocations.end());
+ return std::move(DeclarationLocations);
+ }
+
+ bool handleDeclOccurence(const Decl* D, index::SymbolRoleSet Roles,
+ ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
+ index::IndexDataConsumer::ASTNodeInfo ASTNode) override
+ {
+ if (isSearchedLocation(FID, Offset)) {
+ addDeclarationLocation(D->getSourceRange());
+ }
+ return true;
+ }
+
+private:
+ bool isSearchedLocation(FileID FID, unsigned Offset) const {
+ const SourceManager &SourceMgr = Unit.getSourceManager();
+ return SourceMgr.getFileOffset(SearchedLocation) == Offset
+ && SourceMgr.getFileID(SearchedLocation) == FID;
+ }
+
+ void addDeclarationLocation(const SourceRange& ValSourceRange) {
+ const SourceManager& SourceMgr = Unit.getSourceManager();
+ const LangOptions& LangOpts = Unit.getLangOpts();
+ SourceLocation LocStart = ValSourceRange.getBegin();
+ SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(),
+ 0, SourceMgr, LangOpts);
+ Position P1;
+ P1.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
+ P1.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+ Position P2;
+ P2.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
+ P2.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+ Range R = { P1, P2 };
+ Location L;
+ L.uri = URI::fromFile(
+ SourceMgr.getFilename(SourceMgr.getSpellingLoc(LocStart)));
+ L.range = R;
+ DeclarationLocations.push_back(L);
+ }
+
+ void finish() override
+ {
+ // Also handle possible macro at the searched location.
+ Token Result;
+ if (!Lexer::getRawToken(SearchedLocation, Result, Unit.getSourceManager(),
+ Unit.getASTContext().getLangOpts(), false)) {
+ if (Result.is(tok::raw_identifier)) {
+ Unit.getPreprocessor().LookUpIdentifierInfo(Result);
+ }
+ IdentifierInfo* IdentifierInfo = Result.getIdentifierInfo();
+ if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) {
+ std::pair<FileID, unsigned int> DecLoc =
+ Unit.getSourceManager().getDecomposedExpansionLoc(SearchedLocation);
+ // Get the definition just before the searched location so that a macro
+ // referenced in a '#undef MACRO' can still be found.
+ SourceLocation BeforeSearchedLocation = Unit.getLocation(
+ Unit.getSourceManager().getFileEntryForID(DecLoc.first),
+ DecLoc.second - 1);
+ MacroDefinition MacroDef =
+ Unit.getPreprocessor().getMacroDefinitionAtLoc(IdentifierInfo,
+ BeforeSearchedLocation);
+ MacroInfo* MacroInf = MacroDef.getMacroInfo();
+ if (MacroInf) {
+ addDeclarationLocation(
+ SourceRange(MacroInf->getDefinitionLoc(),
+ MacroInf->getDefinitionEndLoc()));
+ }
+ }
+ }
+ }
+};
+} // namespace
+
+std::vector<Location> ClangdUnit::findDefinitions(Position Pos) {
+ const FileEntry *FE = Unit->getFileManager().getFile(Unit->getMainFileName());
+ if (!FE)
+ return {};
+
+ SourceLocation SourceLocationBeg = getBeginningOfIdentifier(Pos, FE);
+
+ auto DeclLocationsFinder = std::make_shared<DeclarationLocationsFinder>(llvm::errs(),
+ SourceLocationBeg, *Unit);
+ index::IndexingOptions IndexOpts;
+ IndexOpts.SystemSymbolFilter =
+ index::IndexingOptions::SystemSymbolFilterKind::All;
+ IndexOpts.IndexFunctionLocals = true;
+ index::indexASTUnit(*Unit, DeclLocationsFinder, IndexOpts);
+
+ return DeclLocationsFinder->takeLocations();
+}
+
+SourceLocation ClangdUnit::getBeginningOfIdentifier(const Position &Pos,
+ const FileEntry *FE) const {
+ // The language server protocol uses zero-based line and column numbers.
+ // Clang uses one-based numbers.
+ SourceLocation InputLocation = Unit->getLocation(FE, Pos.line + 1,
+ Pos.character + 1);
+
+ if (Pos.character == 0) {
+ return InputLocation;
+ }
+
+ // This handle cases where the position is in the middle of a token or right
+ // after the end of a token. In theory we could just use GetBeginningOfToken
+ // to find the start of the token at the input position, but this doesn't
+ // work when right after the end, i.e. foo|.
+ // So try to go back by one and see if we're still inside the an identifier
+ // token. If so, Take the beginning of this token.
+ // (It should be the same identifier because you can't have two adjacent
+ // identifiers without another token in between.)
+ SourceLocation PeekBeforeLocation = Unit->getLocation(FE, Pos.line + 1,
+ Pos.character);
+ const SourceManager &SourceMgr = Unit->getSourceManager();
+ Token Result;
+ Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr,
+ Unit->getASTContext().getLangOpts(), false);
+ if (Result.is(tok::raw_identifier)) {
+ return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr,
+ Unit->getASTContext().getLangOpts());
+ }
+
+ return InputLocation;
+}
diff --git a/clang-tools-extra/clangd/ClangdUnit.h b/clang-tools-extra/clangd/ClangdUnit.h
index 736a8e2bc48..81d1ba84ee3 100644
--- a/clang-tools-extra/clangd/ClangdUnit.h
+++ b/clang-tools-extra/clangd/ClangdUnit.h
@@ -59,6 +59,8 @@ public:
std::vector<CompletionItem>
codeComplete(StringRef Contents, Position Pos,
IntrusiveRefCntPtr<vfs::FileSystem> VFS);
+ /// Get definition of symbol at a specified \p Line and \p Column in \p File.
+ std::vector<Location> findDefinitions(Position Pos);
/// Returns diagnostics and corresponding FixIts for each diagnostic that are
/// located in the current file.
std::vector<DiagWithFixIts> getLocalDiagnostics() const;
@@ -71,6 +73,8 @@ private:
Path FileName;
std::unique_ptr<ASTUnit> Unit;
std::shared_ptr<PCHContainerOperations> PCHs;
+
+ SourceLocation getBeginningOfIdentifier(const Position& Pos, const FileEntry* FE) const;
};
} // namespace clangd
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index 016d8fd5f3f..17618f3a1c3 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -54,7 +54,7 @@ URI URI::parse(llvm::yaml::ScalarNode *Param) {
}
std::string URI::unparse(const URI &U) {
- return U.uri;
+ return "\"" + U.uri + "\"";
}
llvm::Optional<TextDocumentIdentifier>
@@ -162,6 +162,14 @@ std::string Range::unparse(const Range &P) {
return Result;
}
+std::string Location::unparse(const Location &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result) << llvm::format(
+ R"({"uri": %s, "range": %s})", URI::unparse(P.uri).c_str(),
+ Range::unparse(P.range).c_str());
+ return Result;
+}
+
llvm::Optional<TextDocumentItem>
TextDocumentItem::parse(llvm::yaml::MappingNode *Params) {
TextDocumentItem Result;
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 8fd2ccc03c0..2e0c33d6d90 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -38,6 +38,18 @@ struct URI {
static URI parse(llvm::yaml::ScalarNode *Param);
static std::string unparse(const URI &U);
+
+ friend bool operator==(const URI &LHS, const URI &RHS) {
+ return LHS.uri == RHS.uri;
+ }
+
+ friend bool operator!=(const URI &LHS, const URI &RHS) {
+ return !(LHS == RHS);
+ }
+
+ friend bool operator<(const URI &LHS, const URI &RHS) {
+ return LHS.uri < RHS.uri;
+ }
};
struct TextDocumentIdentifier {
@@ -86,6 +98,26 @@ struct Range {
static std::string unparse(const Range &P);
};
+struct Location {
+ /// The text document's URI.
+ URI uri;
+ Range range;
+
+ friend bool operator==(const Location &LHS, const Location &RHS) {
+ return LHS.uri == RHS.uri && LHS.range == RHS.range;
+ }
+
+ friend bool operator!=(const Location &LHS, const Location &RHS) {
+ return !(LHS == RHS);
+ }
+
+ friend bool operator<(const Location &LHS, const Location &RHS) {
+ return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range);
+ }
+
+ static std::string unparse(const Location &P);
+};
+
struct TextEdit {
/// The range of the text document to be manipulated. To insert
/// text into a document create a range where start === end.
diff --git a/clang-tools-extra/clangd/ProtocolHandlers.cpp b/clang-tools-extra/clangd/ProtocolHandlers.cpp
index 264ef33e297..b67ea0a8dc6 100644
--- a/clang-tools-extra/clangd/ProtocolHandlers.cpp
+++ b/clang-tools-extra/clangd/ProtocolHandlers.cpp
@@ -186,6 +186,24 @@ private:
ProtocolCallbacks &Callbacks;
};
+struct GotoDefinitionHandler : Handler {
+ GotoDefinitionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ auto TDPP = TextDocumentPositionParams::parse(Params);
+ if (!TDPP) {
+ Output.log("Failed to decode TextDocumentPositionParams!\n");
+ return;
+ }
+
+ Callbacks.onGoToDefinition(*TDPP, ID, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
} // namespace
void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher,
@@ -219,4 +237,6 @@ void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher,
Dispatcher.registerHandler(
"textDocument/completion",
llvm::make_unique<CompletionHandler>(Out, Callbacks));
+ Dispatcher.registerHandler("textDocument/definition",
+ llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
}
diff --git a/clang-tools-extra/clangd/ProtocolHandlers.h b/clang-tools-extra/clangd/ProtocolHandlers.h
index 2fb3db81bf3..0ee0383a4d2 100644
--- a/clang-tools-extra/clangd/ProtocolHandlers.h
+++ b/clang-tools-extra/clangd/ProtocolHandlers.h
@@ -46,6 +46,8 @@ public:
JSONOutput &Out) = 0;
virtual void onCompletion(TextDocumentPositionParams Params, StringRef ID,
JSONOutput &Out) = 0;
+ virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
+ JSONOutput &Out) = 0;
};
void regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
diff --git a/clang-tools-extra/test/clangd/definitions.test b/clang-tools-extra/test/clangd/definitions.test
new file mode 100644
index 00000000000..57e0dcecdc7
--- /dev/null
+++ b/clang-tools-extra/test/clangd/definitions.test
@@ -0,0 +1,168 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s
+# It is absolutely vital that this file has CRLF line endings.
+#
+Content-Length: 125
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+
+Content-Length: 172
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}}
+# Go to local variable
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}}
+# Go to local variable, end of token
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}
+
+Content-Length: 214
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n Foo bar = { x : 1 };\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}}
+# Go to field, GNU old-style field designator
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}
+
+Content-Length: 215
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n Foo baz = { .x = 2 };\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}}
+# Go to field, field designator
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}
+
+Content-Length: 187
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int main() {\n main();\n return 0;\n}"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}}
+# Go to function declaration, function call
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]}
+
+Content-Length: 208
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct Foo {\n};\nint main() {\n Foo bar;\n return 0;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}}
+# Go to struct declaration, new struct instance
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 1, "character": 1}}}]}
+
+Content-Length: 231
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace n1 {\nstruct Foo {\n};\n}\nint main() {\n n1::Foo bar;\n return 0;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}}
+# Go to struct declaration, new struct instance, qualified name
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]}
+
+Content-Length: 215
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct Foo {\n int x;\n};\nint main() {\n Foo bar;\n bar.x;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}
+# Go to field declaration, field reference
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 7}}}]}
+
+Content-Length: 220
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n void x();\n};\nint main() {\n Foo bar;\n bar.x();\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}
+# Go to method declaration, method call
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 10}}}]}
+
+Content-Length: 240
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n};\ntypedef Foo TypedefFoo;\nint main() {\n TypedefFoo bar;\n return 0;\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}}
+# Go to typedef
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 0}, "end": {"line": 2, "character": 22}}}]}
+
+Content-Length: 254
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n return 0;\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}}
+# Go to template type parameter. Fails until clangIndex is modified to handle those.
+# no-CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0, "character": 34}}}]}
+
+Content-Length: 256
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\nstatic void bar() {}\n};\n}\nint main() {\n ns::Foo::bar();\n return 0;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}}
+# Go to namespace, static method call
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 4, "character": 1}}}]}
+
+Content-Length: 265
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\n int field;\n Foo(int param) : field(param) {}\n};\n}\nint main() {\n return 0;\n}\n"}]}}
+
+Content-Length: 149
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}}
+# Go to field, member initializer
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 2}, "end": {"line": 2, "character": 11}}}]}
+
+Content-Length: 204
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define MY_MACRO 0\nint main() {\n return MY_MACRO;\n}\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":9}}}
+# Go to macro
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 18}}}]}
+
+Content-Length: 217
+
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}
+# Go to macro, re-defined later
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 13}}}]}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}}
+# Go to macro, undefined later
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}
+# Go to macro, being undefined
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]}
+
+Content-Length: 44
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
diff --git a/clang-tools-extra/test/clangd/formatting.test b/clang-tools-extra/test/clangd/formatting.test
index d0a0cd50541..08be62937db 100644
--- a/clang-tools-extra/test/clangd/formatting.test
+++ b/clang-tools-extra/test/clangd/formatting.test
@@ -4,14 +4,15 @@
Content-Length: 125
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
-# CHECK: Content-Length: 424
+# CHECK: Content-Length: 462
# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
# CHECK: "textDocumentSync": 1,
# CHECK: "documentFormattingProvider": true,
# CHECK: "documentRangeFormattingProvider": true,
# CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
# CHECK: "codeActionProvider": true,
-# CHECK: "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}
+# CHECK: "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},
+# CHECK: "definitionProvider": true
# CHECK: }}}
#
Content-Length: 193
OpenPOWER on IntegriCloud