1 //===--- SemanticSelection.cpp -----------------------------------*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 #include "SemanticSelection.h" 9 #include "ParsedAST.h" 10 #include "Protocol.h" 11 #include "Selection.h" 12 #include "SourceCode.h" 13 #include "clang/AST/DeclBase.h" 14 #include "clang/Basic/SourceLocation.h" 15 #include "llvm/ADT/ArrayRef.h" 16 #include "llvm/Support/Error.h" 17 18 namespace clang { 19 namespace clangd { 20 namespace { 21 // Adds Range \p R to the Result if it is distinct from the last added Range. 22 // Assumes that only consecutive ranges can coincide. 23 void addIfDistinct(const Range &R, std::vector<Range> &Result) { 24 if (Result.empty() || Result.back() != R) { 25 Result.push_back(R); 26 } 27 } 28 } // namespace 29 30 llvm::Expected<SelectionRange> getSemanticRanges(ParsedAST &AST, Position Pos) { 31 std::vector<Range> Ranges; 32 const auto &SM = AST.getSourceManager(); 33 const auto &LangOpts = AST.getLangOpts(); 34 35 auto FID = SM.getMainFileID(); 36 auto Offset = positionToOffset(SM.getBufferData(FID), Pos); 37 if (!Offset) { 38 return Offset.takeError(); 39 } 40 41 // Get node under the cursor. 42 SelectionTree ST = SelectionTree::createRight( 43 AST.getASTContext(), AST.getTokens(), *Offset, *Offset); 44 for (const auto *Node = ST.commonAncestor(); Node != nullptr; 45 Node = Node->Parent) { 46 if (const Decl *D = Node->ASTNode.get<Decl>()) { 47 if (llvm::isa<TranslationUnitDecl>(D)) { 48 break; 49 } 50 } 51 52 auto SR = toHalfOpenFileRange(SM, LangOpts, Node->ASTNode.getSourceRange()); 53 if (!SR.hasValue() || SM.getFileID(SR->getBegin()) != SM.getMainFileID()) { 54 continue; 55 } 56 Range R; 57 R.start = sourceLocToPosition(SM, SR->getBegin()); 58 R.end = sourceLocToPosition(SM, SR->getEnd()); 59 addIfDistinct(R, Ranges); 60 } 61 62 if (Ranges.empty()) { 63 // LSP provides no way to signal "the point is not within a semantic range". 64 // Return an empty range at the point. 65 SelectionRange Empty; 66 Empty.range.start = Empty.range.end = Pos; 67 return std::move(Empty); 68 } 69 70 // Convert to the LSP linked-list representation. 71 SelectionRange Head; 72 Head.range = std::move(Ranges.front()); 73 SelectionRange *Tail = &Head; 74 for (auto &Range : 75 llvm::makeMutableArrayRef(Ranges.data(), Ranges.size()).drop_front()) { 76 Tail->parent = std::make_unique<SelectionRange>(); 77 Tail = Tail->parent.get(); 78 Tail->range = std::move(Range); 79 } 80 81 return std::move(Head); 82 } 83 84 } // namespace clangd 85 } // namespace clang 86