Files
clice/include/AST/Selection.h
2025-06-15 15:13:09 +08:00

182 lines
5.0 KiB
C++

#pragma once
#include "clang/AST/ASTTypeTraits.h"
#include "clang/Tooling/Syntax/Tokens.h"
#include <deque>
namespace clice {
// Code Action:
// add implementation in cpp file(important).
// extract implementation to cpp file(important).
// generate virtual function declaration(full qualified?).
// generate c++20 coroutine and awaiter interface.
// expand macro(one step by step).
// invert if.
class CompilationUnit;
namespace {
class SelectionBuilder;
}
class SelectionTree {
friend class SelectionBuilder;
public:
/// The extent to which an selection is covered by the AST node.
enum class CoverageKind : unsigned {
/// For example, if the selection is
///
/// void f() {
/// int x = 1;
/// ^^^
/// }
///
/// The FunctionDecl `f()` and VarDecl `x` would fully cover the selection.
Full,
/// For example, if the selection is
///
/// if (x == 1) {
/// ^^^^^^^^^^^^^
/// int y = 2;
/// }
///
/// The IfStmt would fully cover the selection while the Expr `x == 1` would partially
/// cover the selection.
Partial,
};
/// An AST node is involved in the selection, either selected directly or some descendant node
/// is selected.
struct Node {
/// The AST node that is selected.
clang::DynTypedNode dynNode;
/// The extent to which the selection is covered by the AST node.
CoverageKind kind;
/// In most cases, there is only 1 child in a selected node. Use SmallVector with stack
/// capability 1 to reduce the size of Node.
llvm::SmallVector<const Node*, 1> children;
/// The parent node in the selection tree. nullptr for root node.
Node* parent;
template <typename T, typename... Ts>
bool isOneOf() const {
return dynNode.get<T>() || (dynNode.get<Ts>() || ...);
}
};
/// Construct an empty selection tree.
SelectionTree() = default;
SelectionTree(const SelectionTree&) = delete;
SelectionTree& operator= (const SelectionTree&) = delete;
SelectionTree(SelectionTree&&) = default;
SelectionTree& operator= (SelectionTree&&) = default;
/// Check if there is any selection.
bool hasValue() const {
return root != nullptr;
}
// Return nullptr if there is no selection.
const Node* getRoot() const {
return root;
}
std::deque<Node>& children() {
return storage;
}
const std::deque<Node>& children() const {
return storage;
}
/// Return true to continue the walk, false to stop.
using Walker = llvm::function_ref<bool(const Node*)>;
/// Return true if the walk is completed, false if the walk is interrupted.
bool walkDfs(Walker ops) const {
if(!root)
return true;
llvm::SmallVector<const Node*> stack;
stack.push_back(root);
while(!stack.empty()) {
auto node = stack.pop_back_val();
if(!ops(node))
return false;
for(auto child: node->children) {
stack.push_back(child);
}
}
return true;
}
/// Return true if the walk is completed, false if the walk is interrupted.
bool walkBfs(Walker ops) const {
if(!root)
return true;
std::deque<const Node*> queue;
queue.push_back(root);
while(!queue.empty()) {
auto node = queue.front();
queue.pop_front();
if(!ops(node))
return false;
for(auto child: node->children) {
queue.push_front(child);
}
}
return true;
}
explicit operator bool () const {
return hasValue();
}
void dump(llvm::raw_ostream& os, clang::ASTContext& context) const;
static SelectionTree selectOffsetRange(std::uint32_t begin,
std::uint32_t end,
clang::ASTContext& context,
CompilationUnit& unit) {
return SelectionTree(begin, end, context, unit);
}
static SelectionTree selectToken(const clang::syntax::Token& token,
clang::ASTContext& context,
CompilationUnit& unit);
private:
/// Construct a selection tree from the given source range. `start` and `end` means offset from
/// file start location, these arguments should come from function `SourceConverter::toOffset`.
SelectionTree(std::uint32_t begin,
std::uint32_t end,
clang::ASTContext& context,
CompilationUnit& unit);
// The root node of selection tree.
Node* root;
// The AST nodes was stored in the order from root to leaf.
// Use deque as the stable pointer storage.
std::deque<Node> storage;
};
} // namespace clice