diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -547,14 +547,13 @@ // Pushes a node onto the ancestor stack. Pairs with pop(). // Performs early hit detection for some nodes (on the earlySourceRange). void push(DynTypedNode Node) { - SourceRange Early = earlySourceRange(Node); dlog("{1}push: {0}", printNodeToString(Node, PrintPolicy), indent()); Nodes.emplace_back(); Nodes.back().ASTNode = std::move(Node); Nodes.back().Parent = Stack.top(); Nodes.back().Selected = NoTokens; Stack.push(&Nodes.back()); - claimRange(Early, Nodes.back().Selected); + claimEarlyRange(Node, Nodes.back().Selected); } // Pops a node off the ancestor stack, and finalizes it. Pairs with push(). @@ -579,19 +578,26 @@ // Returns the range of tokens that this node will claim directly, and // is not available to the node's children. // Usually empty, but sometimes children cover tokens but shouldn't own them. - SourceRange earlySourceRange(const DynTypedNode &N) { + void claimEarlyRange(const DynTypedNode &N, + SelectionTree::Selection &Selected) { + auto Claim = [&](SourceRange R) { claimRange(R, Selected); }; if (const Decl *D = N.get()) { - // void [[foo]](); - if (auto *FD = llvm::dyn_cast(D)) - return FD->getNameInfo().getSourceRange(); + if (auto *FD = llvm::dyn_cast(D)) { + // void [[foo]](); + Claim(FD->getNameInfo().getSourceRange()); + // Also claim the parens. Formally, the FunctionTypeLoc is more precise, + // but it's not a very "useful" node (e.g. we can't find its target). + // Associating the parens with the function is what users expect. + Claim(FD->getFunctionTypeLoc().getLParenLoc()); + Claim(FD->getFunctionTypeLoc().getRParenLoc()); + } // int (*[[s]])(); else if (auto *VD = llvm::dyn_cast(D)) - return VD->getLocation(); + Claim(VD->getLocation()); } else if (const auto* CCI = N.get()) { // : [[b_]](42) - return CCI->getMemberLocation(); + Claim(CCI->getMemberLocation()); } - return SourceRange(); } // Perform hit-testing of a complete Node against the selection. diff --git a/clang-tools-extra/clangd/test/hover.test b/clang-tools-extra/clangd/test/hover.test --- a/clang-tools-extra/clangd/test/hover.test +++ b/clang-tools-extra/clangd/test/hover.test @@ -24,7 +24,7 @@ # CHECK-NEXT: } # CHECK-NEXT:} --- -{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":10}}} +{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":0}}} # CHECK: "id": 1, # CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "result": null diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -249,10 +249,10 @@ {"void foo() { [[foo^()]]; /*comment*/^}", "CallExpr"}, // Tricky case: FunctionTypeLoc in FunctionDecl has a hole in it. - {"[[^void]] foo();", "BuiltinTypeLoc"}, - {"[[void foo^()]];", "FunctionProtoTypeLoc"}, - {"[[^void foo^()]];", "FunctionDecl"}, - {"[[void ^foo()]];", "FunctionDecl"}, + {"[[^void]] foo(int);", "BuiltinTypeLoc"}, + {"[[void foo^(int)]];", "FunctionDecl"}, + {"[[^void foo^(int)]];", "FunctionDecl"}, + {"[[void ^foo(int)]];", "FunctionDecl"}, // Tricky case: two VarDecls share a specifier. {"[[int ^a]], b;", "VarDecl"}, {"[[int a, ^b]];", "VarDecl"}, diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -971,7 +971,7 @@ // Not a definition vo^i[[d^ ^f]]^oo(); - [[vo^id ]]foo[[()]] {[[ + [[vo^id ]]foo() {[[ [[(void)(5+3); return;]] }]] @@ -1879,7 +1879,7 @@ // Not a definition vo^i[[d^ ^f]]^oo(); - [[vo^id ]]foo[[()]] {[[ + [[vo^id ]]foo() {[[ [[(void)(5+3); return;]] }]])cpp");