//===--- Selection.cpp ----------------------------------------------------===// // // 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 "Selection.h" #include "AST.h" #include "support/Logger.h" #include "support/Trace.h" #include "clang/AST/ASTConcept.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TokenKinds.h" #include "clang/Lex/Lexer.h" #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <optional> #include <set> #include <string> namespace clang { namespace clangd { namespace { Node; // Measure the fraction of selections that were enabled by recovery AST. void recordMetrics(const SelectionTree &S, const LangOptions &Lang) { … } // Return the range covering a node and all its children. SourceRange getSourceRange(const DynTypedNode &N) { … } // An IntervalSet maintains a set of disjoint subranges of an array. // // Initially, it contains the entire array. // [-----------------------------------------------------------] // // When a range is erased(), it will typically split the array in two. // Claim: [--------------------] // after: [----------------] [-------------------] // // erase() returns the segments actually erased. Given the state above: // Claim: [---------------------------------------] // Out: [---------] [------] // After: [-----] [-----------] // // It is used to track (expanded) tokens not yet associated with an AST node. // On traversing an AST node, its token range is erased from the unclaimed set. // The tokens actually removed are associated with that node, and hit-tested // against the selection to determine whether the node is selected. template <typename T> class IntervalSet { … }; // Sentinel value for the selectedness of a node where we've seen no tokens yet. // This resolves to Unselected if no tokens are ever seen. // But Unselected + Complete -> Partial, while NoTokens + Complete --> Complete. // This value is never exposed publicly. constexpr SelectionTree::Selection NoTokens = …; // Nodes start with NoTokens, and then use this function to aggregate the // selectedness as more tokens are found. void update(SelectionTree::Selection &Result, SelectionTree::Selection New) { … } // As well as comments, don't count semicolons as real tokens. // They're not properly claimed as expr-statement is missing from the AST. bool shouldIgnore(const syntax::Token &Tok) { … } // Determine whether 'Target' is the first expansion of the macro // argument whose top-level spelling location is 'SpellingLoc'. bool isFirstExpansion(FileID Target, SourceLocation SpellingLoc, const SourceManager &SM) { … } // SelectionTester can determine whether a range of tokens from the PP-expanded // stream (corresponding to an AST node) is considered selected. // // When the tokens result from macro expansions, the appropriate tokens in the // main file are examined (macro invocation or args). Similarly for #includes. // However, only the first expansion of a given spelled token is considered // selected. // // It tests each token in the range (not just the endpoints) as contiguous // expanded tokens may not have contiguous spellings (with macros). // // Non-token text, and tokens not modeled in the AST (comments, semicolons) // are ignored when determining selectedness. class SelectionTester { … }; // Show the type of a node for debugging. void printNodeKind(llvm::raw_ostream &OS, const DynTypedNode &N) { … } #ifndef NDEBUG std::string printNodeToString(const DynTypedNode &N, const PrintingPolicy &PP) { std::string S; llvm::raw_string_ostream OS(S); printNodeKind(OS, N); return std::move(OS.str()); } #endif bool isImplicit(const Stmt *S) { … } // We find the selection by visiting written nodes in the AST, looking for nodes // that intersect with the selected character range. // // While traversing, we maintain a parent stack. As nodes pop off the stack, // we decide whether to keep them or not. To be kept, they must either be // selected or contain some nodes that are. // // For simple cases (not inside macros) we prune subtrees that don't intersect. class SelectionVisitor : public RecursiveASTVisitor<SelectionVisitor> { … }; } // namespace llvm::SmallString<256> abbreviatedString(DynTypedNode N, const PrintingPolicy &PP) { … } void SelectionTree::print(llvm::raw_ostream &OS, const SelectionTree::Node &N, int Indent) const { … } std::string SelectionTree::Node::kind() const { … } // Decide which selections emulate a "point" query in between characters. // If it's ambiguous (the neighboring characters are selectable tokens), returns // both possibilities in preference order. // Always returns at least one range - if no tokens touched, and empty range. static llvm::SmallVector<std::pair<unsigned, unsigned>, 2> pointBounds(unsigned Offset, const syntax::TokenBuffer &Tokens) { … } bool SelectionTree::createEach(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End, llvm::function_ref<bool(SelectionTree)> Func) { … } SelectionTree SelectionTree::createRight(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned int Begin, unsigned int End) { … } SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End) : … { … } const Node *SelectionTree::commonAncestor() const { … } const DeclContext &SelectionTree::Node::getDeclContext() const { … } const SelectionTree::Node &SelectionTree::Node::ignoreImplicit() const { … } const SelectionTree::Node &SelectionTree::Node::outerImplicit() const { … } } // namespace clangd } // namespace clang