152 lines
5.8 KiB
C++
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
|
|
|