Mercurial > hg > CbC > CbC_llvm
view clang-tools-extra/clangd/SemanticSelection.cpp @ 252:1f2b6ac9f198 llvm-original
LLVM16-1
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 18 Aug 2023 09:04:13 +0900 |
parents | c4bab56944e8 |
children |
line wrap: on
line source
//===--- SemanticSelection.cpp -----------------------------------*- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "SemanticSelection.h" #include "ParsedAST.h" #include "Protocol.h" #include "Selection.h" #include "SourceCode.h" #include "clang-pseudo/Bracket.h" #include "clang-pseudo/DirectiveTree.h" #include "clang-pseudo/Token.h" #include "clang/AST/DeclBase.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Tooling/Syntax/BuildTree.h" #include "clang/Tooling/Syntax/Nodes.h" #include "clang/Tooling/Syntax/TokenBufferTokenManager.h" #include "clang/Tooling/Syntax/Tree.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" #include <optional> #include <queue> #include <vector> namespace clang { namespace clangd { namespace { // Adds Range \p R to the Result if it is distinct from the last added Range. // Assumes that only consecutive ranges can coincide. void addIfDistinct(const Range &R, std::vector<Range> &Result) { if (Result.empty() || Result.back() != R) { Result.push_back(R); } } std::optional<FoldingRange> toFoldingRange(SourceRange SR, const SourceManager &SM) { const auto Begin = SM.getDecomposedLoc(SR.getBegin()), End = SM.getDecomposedLoc(SR.getEnd()); // Do not produce folding ranges if either range ends is not within the main // file. Macros have their own FileID so this also checks if locations are not // within the macros. if ((Begin.first != SM.getMainFileID()) || (End.first != SM.getMainFileID())) return std::nullopt; FoldingRange Range; Range.startCharacter = SM.getColumnNumber(Begin.first, Begin.second) - 1; Range.startLine = SM.getLineNumber(Begin.first, Begin.second) - 1; Range.endCharacter = SM.getColumnNumber(End.first, End.second) - 1; Range.endLine = SM.getLineNumber(End.first, End.second) - 1; return Range; } std::optional<FoldingRange> extractFoldingRange(const syntax::Node *Node, const syntax::TokenBufferTokenManager &TM) { if (const auto *Stmt = dyn_cast<syntax::CompoundStatement>(Node)) { const auto *LBrace = cast_or_null<syntax::Leaf>( Stmt->findChild(syntax::NodeRole::OpenParen)); // FIXME(kirillbobyrev): This should find the last child. Compound // statements have only one pair of braces so this is valid but for other // node kinds it might not be correct. const auto *RBrace = cast_or_null<syntax::Leaf>( Stmt->findChild(syntax::NodeRole::CloseParen)); if (!LBrace || !RBrace) return std::nullopt; // Fold the entire range within braces, including whitespace. const SourceLocation LBraceLocInfo = TM.getToken(LBrace->getTokenKey())->endLocation(), RBraceLocInfo = TM.getToken(RBrace->getTokenKey())->location(); auto Range = toFoldingRange(SourceRange(LBraceLocInfo, RBraceLocInfo), TM.sourceManager()); // Do not generate folding range for compound statements without any // nodes and newlines. if (Range && Range->startLine != Range->endLine) return Range; } return std::nullopt; } // Traverse the tree and collect folding ranges along the way. std::vector<FoldingRange> collectFoldingRanges(const syntax::Node *Root, const syntax::TokenBufferTokenManager &TM) { std::queue<const syntax::Node *> Nodes; Nodes.push(Root); std::vector<FoldingRange> Result; while (!Nodes.empty()) { const syntax::Node *Node = Nodes.front(); Nodes.pop(); const auto Range = extractFoldingRange(Node, TM); if (Range) Result.push_back(*Range); if (const auto *T = dyn_cast<syntax::Tree>(Node)) for (const auto *NextNode = T->getFirstChild(); NextNode; NextNode = NextNode->getNextSibling()) Nodes.push(NextNode); } return Result; } } // namespace llvm::Expected<SelectionRange> getSemanticRanges(ParsedAST &AST, Position Pos) { std::vector<Range> Ranges; const auto &SM = AST.getSourceManager(); const auto &LangOpts = AST.getLangOpts(); auto FID = SM.getMainFileID(); auto Offset = positionToOffset(SM.getBufferData(FID), Pos); if (!Offset) { return Offset.takeError(); } // Get node under the cursor. SelectionTree ST = SelectionTree::createRight( AST.getASTContext(), AST.getTokens(), *Offset, *Offset); for (const auto *Node = ST.commonAncestor(); Node != nullptr; Node = Node->Parent) { if (const Decl *D = Node->ASTNode.get<Decl>()) { if (llvm::isa<TranslationUnitDecl>(D)) { break; } } auto SR = toHalfOpenFileRange(SM, LangOpts, Node->ASTNode.getSourceRange()); if (!SR || SM.getFileID(SR->getBegin()) != SM.getMainFileID()) { continue; } Range R; R.start = sourceLocToPosition(SM, SR->getBegin()); R.end = sourceLocToPosition(SM, SR->getEnd()); addIfDistinct(R, Ranges); } if (Ranges.empty()) { // LSP provides no way to signal "the point is not within a semantic range". // Return an empty range at the point. SelectionRange Empty; Empty.range.start = Empty.range.end = Pos; return std::move(Empty); } // Convert to the LSP linked-list representation. SelectionRange Head; Head.range = std::move(Ranges.front()); SelectionRange *Tail = &Head; for (auto &Range : llvm::MutableArrayRef(Ranges.data(), Ranges.size()).drop_front()) { Tail->parent = std::make_unique<SelectionRange>(); Tail = Tail->parent.get(); Tail->range = std::move(Range); } return std::move(Head); } // FIXME(kirillbobyrev): Collect comments, PP conditional regions, includes and // other code regions (e.g. public/private/protected sections of classes, // control flow statement bodies). // Related issue: https://github.com/clangd/clangd/issues/310 llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST) { syntax::Arena A; syntax::TokenBufferTokenManager TM(AST.getTokens(), AST.getLangOpts(), AST.getSourceManager()); const auto *SyntaxTree = syntax::buildSyntaxTree(A, TM, AST.getASTContext()); return collectFoldingRanges(SyntaxTree, TM); } // FIXME( usaxena95): Collect PP conditional regions, includes and other code // regions (e.g. public/private/protected sections of classes, control flow // statement bodies). // Related issue: https://github.com/clangd/clangd/issues/310 llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(const std::string &Code, bool LineFoldingOnly) { auto OrigStream = pseudo::lex(Code, clang::pseudo::genericLangOpts()); auto DirectiveStructure = pseudo::DirectiveTree::parse(OrigStream); pseudo::chooseConditionalBranches(DirectiveStructure, OrigStream); // FIXME: Provide ranges in the disabled-PP regions as well. auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream); auto ParseableStream = cook(Preprocessed, clang::pseudo::genericLangOpts()); pseudo::pairBrackets(ParseableStream); std::vector<FoldingRange> Result; auto AddFoldingRange = [&](Position Start, Position End, llvm::StringLiteral Kind) { if (Start.line >= End.line) return; FoldingRange FR; FR.startLine = Start.line; FR.startCharacter = Start.character; FR.endLine = End.line; FR.endCharacter = End.character; FR.kind = Kind.str(); Result.push_back(FR); }; auto OriginalToken = [&](const pseudo::Token &T) { return OrigStream.tokens()[T.OriginalIndex]; }; auto StartOffset = [&](const pseudo::Token &T) { return OriginalToken(T).text().data() - Code.data(); }; auto StartPosition = [&](const pseudo::Token &T) { return offsetToPosition(Code, StartOffset(T)); }; auto EndOffset = [&](const pseudo::Token &T) { return StartOffset(T) + OriginalToken(T).Length; }; auto EndPosition = [&](const pseudo::Token &T) { return offsetToPosition(Code, EndOffset(T)); }; auto Tokens = ParseableStream.tokens(); // Brackets. for (const auto &Tok : Tokens) { if (auto *Paired = Tok.pair()) { // Process only token at the start of the range. Avoid ranges on a single // line. if (Tok.Line < Paired->Line) { Position Start = offsetToPosition(Code, 1 + StartOffset(Tok)); Position End = StartPosition(*Paired); if (LineFoldingOnly) End.line--; AddFoldingRange(Start, End, FoldingRange::REGION_KIND); } } } auto IsBlockComment = [&](const pseudo::Token &T) { assert(T.Kind == tok::comment); return OriginalToken(T).Length >= 2 && Code.substr(StartOffset(T), 2) == "/*"; }; // Multi-line comments. for (auto *T = Tokens.begin(); T != Tokens.end();) { if (T->Kind != tok::comment) { T++; continue; } pseudo::Token *FirstComment = T; // Show starting sentinals (// and /*) of the comment. Position Start = offsetToPosition(Code, 2 + StartOffset(*FirstComment)); pseudo::Token *LastComment = T; Position End = EndPosition(*T); while (T != Tokens.end() && T->Kind == tok::comment && StartPosition(*T).line <= End.line + 1) { End = EndPosition(*T); LastComment = T; T++; } if (IsBlockComment(*FirstComment)) { if (LineFoldingOnly) // Show last line of a block comment. End.line--; if (IsBlockComment(*LastComment)) // Show ending sentinal "*/" of the block comment. End.character -= 2; } AddFoldingRange(Start, End, FoldingRange::COMMENT_KIND); } return Result; } } // namespace clangd } // namespace clang