refactor(cfg)!: move control flow to its own crate. (#3728)

This commit is contained in:
rzvxa 2024-06-18 15:59:29 +00:00
parent 5c38a0fd69
commit 0537d298db
22 changed files with 244 additions and 184 deletions

15
Cargo.lock generated
View file

@ -1348,6 +1348,17 @@ dependencies = [
"serde_json",
]
[[package]]
name = "oxc_cfg"
version = "0.0.0"
dependencies = [
"bitflags 2.5.0",
"itertools 0.13.0",
"oxc_syntax",
"petgraph",
"rustc-hash",
]
[[package]]
name = "oxc_codegen"
version = "0.14.0"
@ -1478,6 +1489,7 @@ dependencies = [
"once_cell",
"oxc_allocator",
"oxc_ast",
"oxc_cfg",
"oxc_codegen",
"oxc_diagnostics",
"oxc_macros",
@ -1652,18 +1664,17 @@ dependencies = [
name = "oxc_semantic"
version = "0.14.0"
dependencies = [
"bitflags 2.5.0",
"indexmap",
"insta",
"itertools 0.13.0",
"oxc_allocator",
"oxc_ast",
"oxc_cfg",
"oxc_diagnostics",
"oxc_index",
"oxc_parser",
"oxc_span",
"oxc_syntax",
"petgraph",
"phf",
"rustc-hash",
"serde",

View file

@ -93,6 +93,7 @@ oxc_module_lexer = { version = "0.14.0", path = "crates/oxc_module_lexe
oxc_isolated_declarations = { version = "0.14.0", path = "crates/oxc_isolated_declarations" }
# publish = false
oxc_cfg = { path = "crates/oxc_cfg" }
oxc_macros = { path = "crates/oxc_macros" }
oxc_linter = { path = "crates/oxc_linter" }
oxc_prettier = { path = "crates/oxc_prettier" }

27
crates/oxc_cfg/Cargo.toml Normal file
View file

@ -0,0 +1,27 @@
[package]
name = "oxc_cfg"
version = "0.0.0"
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
categories.workspace = true
include = ["/src"]
[lints]
workspace = true
[lib]
doctest = true
[dependencies]
oxc_syntax = { workspace = true }
itertools = { workspace = true }
bitflags = { workspace = true }
petgraph = { workspace = true }
rustc-hash = { workspace = true }

View file

@ -40,6 +40,7 @@ impl<'a> Ctx<'a> {
}
pub trait CtxCursor {
#![allow(clippy::return_self_not_must_use)]
/// Marks the break jump position in the current context.
fn mark_break(self, jmp_pos: BasicBlockId) -> Self;
/// Marks the continue jump position in the current context.

View file

@ -4,11 +4,12 @@ use crate::ReturnInstructionKind;
use context::Ctx;
pub use context::{CtxCursor, CtxFlags};
use oxc_syntax::node::AstNodeId;
use petgraph::Direction;
use super::{
AstNodeId, BasicBlock, BasicBlockId, ControlFlowGraph, EdgeType, ErrorEdgeKind, Graph,
Instruction, InstructionKind, IterationInstructionKind, LabeledInstruction,
BasicBlock, BasicBlockId, ControlFlowGraph, EdgeType, ErrorEdgeKind, Graph, Instruction,
InstructionKind, IterationInstructionKind, LabeledInstruction,
};
#[derive(Debug, Default)]
@ -70,7 +71,8 @@ impl<'a> ControlFlowGraphBuilder<'a> {
self.new_basic_block_normal()
}
/// # Panics if there is no error harness to attach to.
/// # Panics
/// if there is no error harness to attach to.
#[must_use]
pub fn new_basic_block_normal(&mut self) -> BasicBlockId {
let graph_ix = self.new_basic_block();
@ -122,7 +124,8 @@ impl<'a> ControlFlowGraphBuilder<'a> {
graph_ix
}
/// # Panics if there is no error harness pushed onto the stack,
/// # Panics
/// if there is no error harness pushed onto the stack,
/// Or last harness doesn't match the expected `BasicBlockId`.
pub fn release_error_harness(&mut self, expect: BasicBlockId) {
let harness = self
@ -152,7 +155,8 @@ impl<'a> ControlFlowGraphBuilder<'a> {
debug_assert!(result.as_ref().is_some_and(Option::is_none));
}
/// # Panics if last finalizer doesn't match the expected `BasicBlockId`.
/// # Panics
/// if last finalizer doesn't match the expected `BasicBlockId`.
pub fn release_finalizer(&mut self, expect: BasicBlockId) {
// return early if there is no finalizer.
let Some(finalizer) = self.finalizers.pop() else { return };
@ -216,6 +220,7 @@ impl<'a> ControlFlowGraphBuilder<'a> {
);
}
/// # Panics
#[inline]
pub(self) fn push_instruction(&mut self, kind: InstructionKind, node_id: Option<AstNodeId>) {
self.push_instruction_to(self.current_node_ix, kind, node_id);

80
crates/oxc_cfg/src/dot.rs Normal file
View file

@ -0,0 +1,80 @@
// use oxc_ast::{
// ast::{BreakStatement, ContinueStatement},
// AstKind,
// };
use petgraph::{
dot::{Config, Dot},
visit::EdgeRef,
};
use crate::{
BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind, LabeledInstruction,
ReturnInstructionKind,
};
use super::IterationInstructionKind;
pub trait DisplayDot {
fn display_dot(&self) -> String;
}
impl DisplayDot for ControlFlowGraph {
fn display_dot(&self) -> String {
format!(
"{:?}",
Dot::with_attr_getters(
&self.graph,
&[Config::EdgeNoLabel, Config::NodeNoLabel],
&|_graph, edge| {
let weight = edge.weight();
let label = format!("label = \"{weight:?}\" ");
if matches!(weight, EdgeType::Unreachable)
|| self.basic_block(edge.source()).unreachable
{
format!("{label}, style = \"dotted\" ")
} else {
label
}
},
&|_graph, node| format!(
"label = {:?} ",
self.basic_blocks[*node.1].display_dot().trim()
),
)
)
}
}
impl DisplayDot for BasicBlock {
fn display_dot(&self) -> String {
self.instructions().iter().fold(String::new(), |mut acc, it| {
acc.push_str(it.display_dot().as_str());
acc.push('\n');
acc
})
}
}
impl DisplayDot for Instruction {
fn display_dot(&self) -> String {
match self.kind {
InstructionKind::Statement => "statement",
InstructionKind::Unreachable => "unreachable",
InstructionKind::Throw => "throw",
InstructionKind::Condition => "condition",
InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
InstructionKind::Break(LabeledInstruction::Labeled) => "break <label>",
InstructionKind::Break(LabeledInstruction::Unlabeled) => "break",
InstructionKind::Continue(LabeledInstruction::Labeled) => "continue <label>",
InstructionKind::Continue(LabeledInstruction::Unlabeled) => "continue",
InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => {
"return <implicit undefined>"
}
InstructionKind::Return(ReturnInstructionKind::NotImplicitUndefined) => {
"return <value>"
}
}
.to_string()
}
}

View file

@ -3,7 +3,7 @@ mod dot;
pub mod visit;
use itertools::Itertools;
use oxc_ast::AstKind;
use oxc_syntax::node::AstNodeId;
use petgraph::{
stable_graph::NodeIndex,
visit::{depth_first_search, Control, DfsEvent, EdgeRef},
@ -18,10 +18,8 @@ pub mod graph {
}
}
use crate::{AstNodeId, AstNodes};
pub(crate) use builder::{ControlFlowGraphBuilder, CtxCursor, CtxFlags};
pub use dot::{DebugDot, DebugDotContext, DisplayDot};
pub use builder::{ControlFlowGraphBuilder, CtxCursor, CtxFlags};
pub use dot::DisplayDot;
pub type BasicBlockId = NodeIndex;
@ -113,6 +111,12 @@ pub enum ErrorEdgeKind {
Implicit,
}
pub enum EvalConstConditionResult {
NotFound,
Fail,
Eval(bool),
}
#[derive(Debug)]
pub struct ControlFlowGraph {
pub graph: Graph<usize, EdgeType>,
@ -172,32 +176,14 @@ impl ControlFlowGraph {
/// Returns `None` the given node isn't the cyclic point of an infinite loop.
/// Otherwise returns `Some(loop_start, loop_end)`.
pub fn is_infinite_loop_start(
pub fn is_infinite_loop_start<F>(
&self,
node: BasicBlockId,
nodes: &AstNodes,
) -> Option<(BasicBlockId, BasicBlockId)> {
enum EvalConstConditionResult {
NotFound,
Fail,
Eval(bool),
}
fn try_eval_const_condition(
instruction: &Instruction,
nodes: &AstNodes,
) -> EvalConstConditionResult {
use EvalConstConditionResult::{Eval, Fail, NotFound};
match instruction {
Instruction { kind: InstructionKind::Condition, node_id: Some(id) } => {
match nodes.kind(*id) {
AstKind::BooleanLiteral(lit) => Eval(lit.value),
_ => Fail,
}
}
_ => NotFound,
}
}
try_eval_const_condition: F,
) -> Option<(BasicBlockId, BasicBlockId)>
where
F: Fn(&Instruction) -> EvalConstConditionResult,
{
fn get_jump_target(
graph: &Graph<usize, EdgeType>,
node: BasicBlockId,
@ -239,15 +225,14 @@ impl ControlFlowGraph {
// if there is exactly one and it is a condition instruction we are in a loop so we
// check the condition to infer if it is always true.
if let EvalConstConditionResult::Eval(true) =
try_eval_const_condition(only_instruction, nodes)
{
if let EvalConstConditionResult::Eval(true) = try_eval_const_condition(only_instruction) {
get_jump_target(&self.graph, node).map(|it| (it, node))
} else if let EvalConstConditionResult::Eval(true) =
self.basic_block(backedge.source()).instructions().iter().exactly_one().map_or_else(
|_| EvalConstConditionResult::NotFound,
|it| try_eval_const_condition(it, nodes),
)
} else if let EvalConstConditionResult::Eval(true) = self
.basic_block(backedge.source())
.instructions()
.iter()
.exactly_one()
.map_or_else(|_| EvalConstConditionResult::NotFound, try_eval_const_condition)
{
get_jump_target(&self.graph, node).map(|it| (node, it))
} else {

View file

@ -20,16 +20,17 @@ workspace = true
doctest = false
[dependencies]
oxc_allocator = { workspace = true }
oxc_parser = { workspace = true }
oxc_span = { workspace = true }
oxc_ast = { workspace = true }
oxc_diagnostics = { workspace = true }
oxc_macros = { workspace = true }
oxc_semantic = { workspace = true }
oxc_syntax = { workspace = true }
oxc_codegen = { workspace = true }
oxc_resolver = { workspace = true }
oxc_allocator = { workspace = true }
oxc_parser = { workspace = true }
oxc_span = { workspace = true }
oxc_ast = { workspace = true }
oxc_cfg = { workspace = true }
oxc_diagnostics = { workspace = true }
oxc_macros = { workspace = true }
oxc_semantic = { workspace = true }
oxc_syntax = { workspace = true }
oxc_codegen = { workspace = true }
oxc_resolver = { workspace = true }
rayon = { workspace = true }
lazy_static = { workspace = true }
@ -51,6 +52,6 @@ json-strip-comments = { workspace = true }
schemars = { workspace = true, features = ["indexmap2"] }
[dev-dependencies]
static_assertions = { workspace = true }
insta = { workspace = true }
project-root = { workspace = true }
static_assertions = { workspace = true }
insta = { workspace = true }
project-root = { workspace = true }

View file

@ -7,11 +7,11 @@ use oxc_ast::{
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{
control_flow::graph::visit::neighbors_filtered_by_edge_weight, EdgeType, InstructionKind,
use oxc_cfg::{
graph::visit::neighbors_filtered_by_edge_weight, EdgeType, InstructionKind,
ReturnInstructionKind,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};

View file

@ -5,15 +5,15 @@ use oxc_ast::{
ast::{Statement, SwitchCase, SwitchStatement},
AstKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{
control_flow::graph::{
use oxc_cfg::{
graph::{
visit::{neighbors_filtered_by_edge_weight, EdgeRef},
Direction,
},
BasicBlockId, EdgeType, ErrorEdgeKind, InstructionKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use regex::Regex;
use rustc_hash::{FxHashMap, FxHashSet};

View file

@ -1,4 +1,5 @@
use oxc_diagnostics::OxcDiagnostic;
use oxc_semantic::AstNodeId;
use std::collections::{HashMap, HashSet};
@ -6,11 +7,11 @@ use oxc_ast::{
ast::{Expression, MethodDefinitionKind},
AstKind,
};
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{
control_flow::graph::visit::{neighbors_filtered_by_edge_weight, EdgeRef},
AstNodeId, BasicBlockId, ControlFlowGraph, EdgeType,
use oxc_cfg::{
graph::visit::{neighbors_filtered_by_edge_weight, EdgeRef},
BasicBlockId, ControlFlowGraph, EdgeType, ErrorEdgeKind,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
@ -187,8 +188,7 @@ impl NoThisBeforeSuper {
if cfg.graph.edges(*basic_block_id).any(|it| {
matches!(
it.weight(),
EdgeType::Error(oxc_semantic::ErrorEdgeKind::Explicit)
| EdgeType::Finalize
EdgeType::Error(ErrorEdgeKind::Explicit) | EdgeType::Finalize
)
}) {
(DefinitelyCallsThisBeforeSuper::Maybe(*basic_block_id), false)
@ -227,8 +227,7 @@ impl NoThisBeforeSuper {
DefinitelyCallsThisBeforeSuper::No => false,
DefinitelyCallsThisBeforeSuper::Maybe(id) => cfg.graph.edges(id).any(|edge| {
let weight = edge.weight();
let is_explicit_error =
matches!(weight, EdgeType::Error(oxc_semantic::ErrorEdgeKind::Explicit));
let is_explicit_error = matches!(weight, EdgeType::Error(ErrorEdgeKind::Explicit));
if is_explicit_error || matches!(weight, EdgeType::Finalize) {
Self::check_for_violation(
cfg,

View file

@ -1,13 +1,13 @@
use oxc_ast::{ast::VariableDeclarationKind, AstKind};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{
control_flow::graph::{
use oxc_cfg::{
graph::{
visit::{depth_first_search, Control, DfsEvent, EdgeRef},
Direction,
},
EdgeType, ErrorEdgeKind, InstructionKind,
EdgeType, ErrorEdgeKind, Instruction, InstructionKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule};
@ -56,7 +56,18 @@ impl Rule for NoUnreachable {
unreachables[node.index()] = unreachable;
if !unreachable {
if let Some(it) = cfg.is_infinite_loop_start(node, nodes) {
if let Some(it) = cfg.is_infinite_loop_start(node, |instruction| {
use oxc_cfg::EvalConstConditionResult::{Eval, Fail, NotFound};
match instruction {
Instruction { kind: InstructionKind::Condition, node_id: Some(id) } => {
match nodes.kind(*id) {
AstKind::BooleanLiteral(lit) => Eval(lit.value),
_ => Fail,
}
}
_ => NotFound,
}
}) {
infinite_loops.push(it);
}
}

View file

@ -1,11 +1,11 @@
use oxc_ast::{ast::Expression, AstKind};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{
control_flow::graph::visit::neighbors_filtered_by_edge_weight, EdgeType, Instruction,
InstructionKind, ReturnInstructionKind,
use oxc_cfg::{
graph::visit::neighbors_filtered_by_edge_weight, EdgeType, Instruction, InstructionKind,
ReturnInstructionKind,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use crate::{

View file

@ -2,11 +2,12 @@ use oxc_ast::{
ast::{ArrowFunctionExpression, Function},
AstKind,
};
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{
control_flow::graph::{algo, visit::Control},
AstNodeId, AstNodes, EdgeType, ErrorEdgeKind, InstructionKind,
use oxc_cfg::{
graph::{algo, visit::Control},
EdgeType, ErrorEdgeKind, InstructionKind,
};
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{AstNodeId, AstNodes};
use oxc_span::{Atom, CompactStr};
use oxc_syntax::operator::AssignmentOperator;

View file

@ -19,20 +19,19 @@ workspace = true
doctest = false
[dependencies]
oxc_span = { workspace = true }
oxc_ast = { workspace = true }
oxc_syntax = { workspace = true }
oxc_diagnostics = { workspace = true }
oxc_index = { workspace = true }
oxc_allocator = { workspace = true }
oxc_span = { workspace = true }
oxc_ast = { workspace = true }
oxc_syntax = { workspace = true }
oxc_cfg = { workspace = true }
oxc_diagnostics = { workspace = true }
oxc_index = { workspace = true }
oxc_allocator = { workspace = true }
indexmap = { workspace = true }
phf = { workspace = true, features = ["macros"] }
rustc-hash = { workspace = true }
serde = { workspace = true, features = ["derive"], optional = true }
petgraph = { workspace = true }
itertools = { workspace = true }
bitflags = { workspace = true }
tsify = { workspace = true, optional = true }
wasm-bindgen = { workspace = true, optional = true }

View file

@ -3,13 +3,16 @@ use std::{collections::HashMap, env, path::Path, sync::Arc};
use itertools::Itertools;
use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_semantic::{DebugDot, DisplayDot, EdgeType, SemanticBuilder};
use oxc_span::SourceType;
use petgraph::{
dot::{Config, Dot},
visit::EdgeRef,
use oxc_cfg::{
graph::{
dot::{Config, Dot},
visit::EdgeRef,
},
DisplayDot, EdgeType,
};
use oxc_parser::Parser;
use oxc_semantic::{dot::DebugDot, SemanticBuilder};
use oxc_span::SourceType;
// Instruction:
// 1. create a `test.js`,

View file

@ -4,6 +4,10 @@ use std::{cell::RefCell, path::PathBuf, sync::Arc};
#[allow(clippy::wildcard_imports)]
use oxc_ast::{ast::*, AstKind, Trivias, Visit};
use oxc_cfg::{
ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind,
IterationInstructionKind, ReturnInstructionKind,
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{CompactStr, SourceType, Span};
use oxc_syntax::{
@ -16,10 +20,6 @@ use crate::{
binder::Binder,
checker,
class::ClassTableBuilder,
control_flow::{
ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind,
IterationInstructionKind, ReturnInstructionKind,
},
diagnostics::redeclaration,
jsdoc::JSDocBuilder,
label::LabelBuilder,

View file

@ -2,18 +2,17 @@ use oxc_ast::{
ast::{BreakStatement, ContinueStatement},
AstKind,
};
use oxc_syntax::node::AstNodeId;
use petgraph::{
dot::{Config, Dot},
visit::EdgeRef,
};
use crate::{
AstNode, AstNodes, BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind,
use oxc_cfg::{
graph::{
dot::{Config, Dot},
visit::EdgeRef,
},
BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind, IterationInstructionKind,
LabeledInstruction, ReturnInstructionKind,
};
use oxc_syntax::node::AstNodeId;
use super::IterationInstructionKind;
use crate::{AstNode, AstNodes};
pub trait DisplayDot {
fn display_dot(&self) -> String;
@ -49,67 +48,6 @@ impl<'a, 'b> From<&'b AstNodes<'a>> for DebugDotContext<'a, 'b> {
}
}
impl DisplayDot for ControlFlowGraph {
fn display_dot(&self) -> String {
format!(
"{:?}",
Dot::with_attr_getters(
&self.graph,
&[Config::EdgeNoLabel, Config::NodeNoLabel],
&|_graph, edge| {
let weight = edge.weight();
let label = format!("label = \"{weight:?}\" ");
if matches!(weight, EdgeType::Unreachable)
|| self.basic_block(edge.source()).unreachable
{
format!("{label}, style = \"dotted\" ")
} else {
label
}
},
&|_graph, node| format!(
"label = {:?} ",
self.basic_blocks[*node.1].display_dot().trim()
),
)
)
}
}
impl DisplayDot for BasicBlock {
fn display_dot(&self) -> String {
self.instructions().iter().fold(String::new(), |mut acc, it| {
acc.push_str(it.display_dot().as_str());
acc.push('\n');
acc
})
}
}
impl DisplayDot for Instruction {
fn display_dot(&self) -> String {
match self.kind {
InstructionKind::Statement => "statement",
InstructionKind::Unreachable => "unreachable",
InstructionKind::Throw => "throw",
InstructionKind::Condition => "condition",
InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
InstructionKind::Break(LabeledInstruction::Labeled) => "break <label>",
InstructionKind::Break(LabeledInstruction::Unlabeled) => "break",
InstructionKind::Continue(LabeledInstruction::Labeled) => "continue <label>",
InstructionKind::Continue(LabeledInstruction::Unlabeled) => "continue",
InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => {
"return <implicit undefined>"
}
InstructionKind::Return(ReturnInstructionKind::NotImplicitUndefined) => {
"return <value>"
}
}
.to_string()
}
}
impl DebugDot for ControlFlowGraph {
fn debug_dot(&self, ctx: DebugDotContext) -> String {
format!(
@ -166,7 +104,7 @@ impl DebugDot for Instruction {
"Iteration({} {} {})",
self.node_id.map_or("None".to_string(), |id| ctx.debug_ast_kind(id)),
if matches!(kind, IterationInstructionKind::Of) { "of" } else { "in" },
// TODO: at this point we can't evaluate this note. needs access to the graph information.
// TODO: at this point we can't evaluate this node. needs access to the graph information.
"expr"
)
}

View file

@ -11,14 +11,16 @@ mod reference;
mod scope;
mod symbol;
pub mod control_flow;
pub mod dot;
use std::sync::Arc;
pub use builder::{SemanticBuilder, SemanticBuilderReturn};
use class::ClassTable;
pub use jsdoc::{JSDoc, JSDocFinder, JSDocTag};
pub use node::{AstNode, AstNodeId, AstNodes};
use oxc_ast::{ast::IdentifierReference, AstKind, Trivias};
use oxc_cfg::ControlFlowGraph;
use oxc_span::SourceType;
pub use oxc_syntax::{
module_record::ModuleRecord,
@ -28,12 +30,6 @@ pub use oxc_syntax::{
use rustc_hash::FxHashSet;
pub use crate::{
control_flow::{
BasicBlock, BasicBlockId, ControlFlowGraph, DebugDot, DebugDotContext, DisplayDot,
EdgeType, ErrorEdgeKind, Instruction, InstructionKind, LabeledInstruction,
ReturnInstructionKind,
},
node::{AstNode, AstNodeId, AstNodes},
reference::{Reference, ReferenceFlag, ReferenceId},
scope::ScopeTree,
symbol::SymbolTable,

View file

@ -1,8 +1,9 @@
use oxc_ast::AstKind;
use oxc_index::IndexVec;
use crate::{control_flow::BasicBlockId, scope::ScopeId};
use crate::scope::ScopeId;
use oxc_cfg::BasicBlockId;
pub use oxc_syntax::node::{AstNodeId, NodeFlags};
/// Semantic node contains all the semantic information about an ast node.

View file

@ -5,8 +5,9 @@ use std::{path::PathBuf, sync::Arc};
use itertools::Itertools;
use oxc_allocator::Allocator;
use oxc_cfg::DisplayDot;
use oxc_diagnostics::{Error, NamedSource, OxcDiagnostic};
use oxc_semantic::{DebugDot, DisplayDot, Semantic, SemanticBuilder};
use oxc_semantic::{dot::DebugDot, Semantic, SemanticBuilder};
use oxc_span::SourceType;
pub use class_tester::ClassTester;