clang-tools  14.0.0git
SemanticSelection.cpp
Go to the documentation of this file.
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 
9 #include "SemanticSelection.h"
10 #include "FindSymbols.h"
11 #include "ParsedAST.h"
12 #include "Protocol.h"
13 #include "Selection.h"
14 #include "SourceCode.h"
15 #include "clang/AST/DeclBase.h"
16 #include "clang/Basic/SourceLocation.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "clang/Basic/TokenKinds.h"
19 #include "clang/Tooling/Syntax/BuildTree.h"
20 #include "clang/Tooling/Syntax/Nodes.h"
21 #include "clang/Tooling/Syntax/Tree.h"
22 #include "llvm/ADT/ArrayRef.h"
23 #include "llvm/Support/Casting.h"
24 #include "llvm/Support/Error.h"
25 #include <queue>
26 #include <vector>
27 
28 namespace clang {
29 namespace clangd {
30 namespace {
31 
32 // Adds Range \p R to the Result if it is distinct from the last added Range.
33 // Assumes that only consecutive ranges can coincide.
34 void addIfDistinct(const Range &R, std::vector<Range> &Result) {
35  if (Result.empty() || Result.back() != R) {
36  Result.push_back(R);
37  }
38 }
39 
40 llvm::Optional<FoldingRange> toFoldingRange(SourceRange SR,
41  const SourceManager &SM) {
42  const auto Begin = SM.getDecomposedLoc(SR.getBegin()),
43  End = SM.getDecomposedLoc(SR.getEnd());
44  // Do not produce folding ranges if either range ends is not within the main
45  // file. Macros have their own FileID so this also checks if locations are not
46  // within the macros.
47  if ((Begin.first != SM.getMainFileID()) || (End.first != SM.getMainFileID()))
48  return llvm::None;
49  FoldingRange Range;
50  Range.startCharacter = SM.getColumnNumber(Begin.first, Begin.second) - 1;
51  Range.startLine = SM.getLineNumber(Begin.first, Begin.second) - 1;
52  Range.endCharacter = SM.getColumnNumber(End.first, End.second) - 1;
53  Range.endLine = SM.getLineNumber(End.first, End.second) - 1;
54  return Range;
55 }
56 
57 llvm::Optional<FoldingRange> extractFoldingRange(const syntax::Node *Node,
58  const SourceManager &SM) {
59  if (const auto *Stmt = dyn_cast<syntax::CompoundStatement>(Node)) {
60  const auto *LBrace = cast_or_null<syntax::Leaf>(
61  Stmt->findChild(syntax::NodeRole::OpenParen));
62  // FIXME(kirillbobyrev): This should find the last child. Compound
63  // statements have only one pair of braces so this is valid but for other
64  // node kinds it might not be correct.
65  const auto *RBrace = cast_or_null<syntax::Leaf>(
66  Stmt->findChild(syntax::NodeRole::CloseParen));
67  if (!LBrace || !RBrace)
68  return llvm::None;
69  // Fold the entire range within braces, including whitespace.
70  const SourceLocation LBraceLocInfo = LBrace->getToken()->endLocation(),
71  RBraceLocInfo = RBrace->getToken()->location();
72  auto Range = toFoldingRange(SourceRange(LBraceLocInfo, RBraceLocInfo), SM);
73  // Do not generate folding range for compound statements without any
74  // nodes and newlines.
75  if (Range && Range->startLine != Range->endLine)
76  return Range;
77  }
78  return llvm::None;
79 }
80 
81 // Traverse the tree and collect folding ranges along the way.
82 std::vector<FoldingRange> collectFoldingRanges(const syntax::Node *Root,
83  const SourceManager &SM) {
84  std::queue<const syntax::Node *> Nodes;
85  Nodes.push(Root);
86  std::vector<FoldingRange> Result;
87  while (!Nodes.empty()) {
88  const syntax::Node *Node = Nodes.front();
89  Nodes.pop();
90  const auto Range = extractFoldingRange(Node, SM);
91  if (Range)
92  Result.push_back(*Range);
93  if (const auto *T = dyn_cast<syntax::Tree>(Node))
94  for (const auto *NextNode = T->getFirstChild(); NextNode;
95  NextNode = NextNode->getNextSibling())
96  Nodes.push(NextNode);
97  }
98  return Result;
99 }
100 
101 } // namespace
102 
103 llvm::Expected<SelectionRange> getSemanticRanges(ParsedAST &AST, Position Pos) {
104  std::vector<Range> Ranges;
105  const auto &SM = AST.getSourceManager();
106  const auto &LangOpts = AST.getLangOpts();
107 
108  auto FID = SM.getMainFileID();
109  auto Offset = positionToOffset(SM.getBufferData(FID), Pos);
110  if (!Offset) {
111  return Offset.takeError();
112  }
113 
114  // Get node under the cursor.
116  AST.getASTContext(), AST.getTokens(), *Offset, *Offset);
117  for (const auto *Node = ST.commonAncestor(); Node != nullptr;
118  Node = Node->Parent) {
119  if (const Decl *D = Node->ASTNode.get<Decl>()) {
120  if (llvm::isa<TranslationUnitDecl>(D)) {
121  break;
122  }
123  }
124 
125  auto SR = toHalfOpenFileRange(SM, LangOpts, Node->ASTNode.getSourceRange());
126  if (!SR.hasValue() || SM.getFileID(SR->getBegin()) != SM.getMainFileID()) {
127  continue;
128  }
129  Range R;
130  R.start = sourceLocToPosition(SM, SR->getBegin());
131  R.end = sourceLocToPosition(SM, SR->getEnd());
132  addIfDistinct(R, Ranges);
133  }
134 
135  if (Ranges.empty()) {
136  // LSP provides no way to signal "the point is not within a semantic range".
137  // Return an empty range at the point.
139  Empty.range.start = Empty.range.end = Pos;
140  return std::move(Empty);
141  }
142 
143  // Convert to the LSP linked-list representation.
145  Head.range = std::move(Ranges.front());
147  for (auto &Range :
148  llvm::makeMutableArrayRef(Ranges.data(), Ranges.size()).drop_front()) {
149  Tail->parent = std::make_unique<SelectionRange>();
150  Tail = Tail->parent.get();
151  Tail->range = std::move(Range);
152  }
153 
154  return std::move(Head);
155 }
156 
157 // FIXME(kirillbobyrev): Collect comments, PP conditional regions, includes and
158 // other code regions (e.g. public/private/protected sections of classes,
159 // control flow statement bodies).
160 // Related issue: https://github.com/clangd/clangd/issues/310
161 llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST) {
163  const auto *SyntaxTree = syntax::buildSyntaxTree(A, AST.getASTContext());
164  return collectFoldingRanges(SyntaxTree, AST.getSourceManager());
165 }
166 
167 } // namespace clangd
168 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::clangd::Head
@ Head
Definition: FuzzyMatch.h:58
clang::clangd::getFoldingRanges
llvm::Expected< std::vector< FoldingRange > > getFoldingRanges(ParsedAST &AST)
Returns a list of ranges whose contents might be collapsible in an editor.
Definition: SemanticSelection.cpp:161
Selection.h
clang::clangd::toHalfOpenFileRange
llvm::Optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
Definition: SourceCode.cpp:424
clang::clangd::ParsedAST::getASTContext
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
Definition: ParsedAST.cpp:539
Root
ASTNode Root
Definition: DumpAST.cpp:332
clang::clangd::Range::start
Position start
The range's start position.
Definition: Protocol.h:179
FindSymbols.h
clang::clangd::getSemanticRanges
llvm::Expected< SelectionRange > getSemanticRanges(ParsedAST &AST, Position Pos)
Returns the list of all interesting ranges around the Position Pos.
Definition: SemanticSelection.cpp:103
clang::clangd::sourceLocToPosition
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
Definition: SourceCode.cpp:216
clang::clangd::Tail
@ Tail
Definition: FuzzyMatch.h:57
clang::clangd::ParsedAST::getLangOpts
const LangOptions & getLangOpts() const
Definition: ParsedAST.h:82
Protocol.h
Offset
size_t Offset
Definition: CodeComplete.cpp:1147
ns1::ns2::A
@ A
Definition: CategoricalFeature.h:3
clang::clangd::Position
Definition: Protocol.h:148
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
clang::clangd::SelectionTree::createRight
static SelectionTree createRight(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End)
Definition: Selection.cpp:853
SemanticSelection.h
clang::clangd::positionToOffset
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
Definition: SourceCode.cpp:175
clang::clangd::Range::end
Position end
The range's end position.
Definition: Protocol.h:182
clang::clangd::SelectionTree
Definition: Selection.h:76
clang::clangd::SelectionRange
Definition: Protocol.h:1658
clang::clangd::SelectionTree::commonAncestor
const Node * commonAncestor() const
Definition: Selection.cpp:883
SourceCode.h
clang::clangd::Empty
@ Empty
Definition: FuzzyMatch.h:42
clang::clangd::Range
Definition: Protocol.h:177
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::ParsedAST::getTokens
const syntax::TokenBuffer & getTokens() const
Tokens recorded while parsing the main file.
Definition: ParsedAST.h:108
Arena
llvm::BumpPtrAllocator Arena
Definition: Serialization.cpp:211
SM
const SourceManager & SM
Definition: IncludeCleaner.cpp:108
clang::clangd::ParsedAST
Stores and provides access to parsed AST.
Definition: ParsedAST.h:49
Pos
Position Pos
Definition: SourceCode.cpp:657
clang::clangd::ParsedAST::getSourceManager
SourceManager & getSourceManager()
Definition: ParsedAST.h:75
ParsedAST.h