Files
clice/include/AST/Selection.h

152 lines
5.8 KiB
C++

#pragma once
#include <stack>
#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<bool(SelectionTree)> 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<const Node*> children;
/// The extent to which this node is covered by the selection.
SelectionKind selected;
clang::DynTypedNode data;
template <typename T>
auto get() const {
return data.get<T>();
}
/// 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<Node> nodes;
const Node* m_root;
clang::PrintingPolicy print_policy;
};
} // namespace clice