#pragma once #include #include "SourceCode.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/PrettyPrinter.h" #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/SmallVector.h" namespace clice { class CompilationUnit; /// A selection can partially or completely cover several AST nodes. /// The SelectionTree contains nodes that are covered, and their parents. /// SelectionTree does not contain all AST nodes, rather only: /// Decl, Stmt, TypeLoc, NestedNamespaceSpecifierLoc, CXXCtorInitializer. /// (These are the nodes with source ranges that fit in DynTypedNode). /// /// Usually commonAncestor() is the place to start: /// - it's the simplest answer to "what node is under the cursor" /// - the selected Expr (for example) can be found by walking up the parent /// chain and checking Node->ASTNode. /// - if you want to traverse the selected nodes, they are all under /// commonAncestor() in the tree. /// /// SelectionTree tries to behave sensibly in the presence of macros, but does /// not model any preprocessor concepts: the output is a subset of the AST. /// When a macro argument is specifically selected, only its first expansion is /// selected in the AST. (Returning a selection forest is unreasonably difficult /// for callers to handle correctly.) /// /// Comments, directives and whitespace are completely ignored. /// Semicolons are also ignored, as the AST generally does not model them well. /// /// The SelectionTree owns the Node structures, but the ASTNode attributes /// point back into the AST it was constructed with. class SelectionTree { public: /// Create selection trees for the given range, and pass them to Func. /// /// There may be multiple possible selection trees: /// - if the range is empty and borders two tokens, a tree for the right token /// and a tree for the left token will be yielded. /// - Func should return true on success (stop) and false on failure (continue) /// /// Always yields at least one tree. If no tokens are touched, it is empty. static bool create_each(CompilationUnit& unit, LocalSourceRange range, llvm::function_ref callback); /// Create a selection tree for the given range. /// /// Where ambiguous (range is empty and borders two tokens), prefer the token /// on the right. static SelectionTree create_right(CompilationUnit& unit, LocalSourceRange range); /// Copies are no good - contain pointers to other nodes. SelectionTree(const SelectionTree&) = delete; SelectionTree& operator= (const SelectionTree&) = delete; /// Moves are OK though - internal storage is pointer-stable when moved. SelectionTree(SelectionTree&&) = default; SelectionTree& operator= (SelectionTree&&) = default; // Describes to what extent an AST node is covered by the selection. enum SelectionKind : unsigned char { // The AST node owns no characters covered by the selection. // Note that characters owned by children don't count: // if (x == 0) scream(); // ^^^^^^ // The IfStmt would be Unselected because all the selected characters are // associated with its children. // (Invisible nodes like ImplicitCastExpr are always unselected). Unselected, // The AST node owns selected characters, but is not completely covered. Partial, // The AST node owns characters, and is covered by the selection. Complete, }; // An AST node that is implicated in the selection. // (Either selected directly, or some descendant is selected). struct Node { /// The parent within the selection tree. nullptr for TranslationUnitDecl. Node* parent; /// Direct children within the selection tree. llvm::SmallVector children; /// The extent to which this node is covered by the selection. SelectionKind selected; clang::DynTypedNode data; template auto get() const { return data.get(); } /// Get the source range of this node. clang::SourceRange source_range() const; /// Printable node kind, like "CXXRecordDecl" or "AutoTypeLoc". std::string kind() const; /// Walk up the AST to get the lexical DeclContext of this Node, which is not /// the node itself. const clang::DeclContext& decl_context() const; /// If this node is a wrapper with no syntax (e.g. implicit cast), return /// its contents. (If multiple wrappers are present, unwraps all of them). const Node& ignore_implicit() const; // If this node is inside a wrapper with no syntax (e.g. implicit cast), // return that wrapper. (If multiple are present, unwraps all of them). const Node& outer_implicit() const; }; // The most specific common ancestor of all the selected nodes. // Returns nullptr if the common ancestor is the root. // (This is to avoid accidentally traversing the TUDecl and thus preamble). const Node* common_ancestor() const; // The selection node corresponding to TranslationUnitDecl. const Node& root() const { return *m_root; } void print(llvm::raw_ostream& os, const Node& node, int indent) const; friend llvm::raw_ostream& operator<< (llvm::raw_ostream& os, const SelectionTree& tree) { tree.print(os, tree.root(), 1); return os; } private: // Creates a selection tree for the given range in the main file. // The range includes bytes [Start, End). SelectionTree(CompilationUnit& unit, LocalSourceRange range); // Stable-pointer storage, FIXME: use memory pool instead? std::deque nodes; const Node* m_root; clang::PrintingPolicy print_policy; }; } // namespace clice