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