Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cfg): color-code edges in CFG dot diagrams #6314

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 123 additions & 17 deletions crates/oxc_cfg/src/dot.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// use oxc_ast::{
// ast::{BreakStatement, ContinueStatement},
// AstKind,
// };
use std::{borrow::Cow, fmt};

use itertools::Itertools as _;
use petgraph::{
dot::{Config, Dot},
visit::EdgeRef,
};
use rustc_hash::FxHashMap;

use super::IterationInstructionKind;
use crate::{
Expand All @@ -26,31 +26,39 @@ impl DisplayDot for ControlFlowGraph {
&[Config::EdgeNoLabel, Config::NodeNoLabel],
&|_graph, edge| {
let weight = edge.weight();
let label = format!("label = \"{weight:?}\" ");
let mut attrs = Attrs::default().with("label", format!("{weight:?}"));

if matches!(weight, EdgeType::Unreachable)
|| self.basic_block(edge.source()).unreachable
{
format!("{label}, style = \"dotted\" ")
} else {
label
attrs += ("style", "dotted");
} else if matches!(weight, EdgeType::Error(_)) {
attrs += ("color", "red");
};

format!("{attrs:?}")
},
&|_graph, node| {
let block = &self.basic_blocks[*node.1];
let mut attrs = Attrs::default().with("label", block.display_dot());

if *node.1 == 0 {
attrs += ("color", "green");
}
if block.unreachable {
attrs += ("style", "dotted");
}

format!("{attrs:?}")
},
&|_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
})
self.instructions().iter().map(DisplayDot::display_dot).join("\n")
}
}

Expand All @@ -77,3 +85,101 @@ impl DisplayDot for Instruction {
.to_string()
}
}

#[derive(Clone)]
pub enum Attr<'a> {
String(Cow<'a, str>),
Identifier(Cow<'a, str>),
Int(i64),
}
impl<'a> Attr<'a> {
#[inline]
#[must_use]
pub fn ident<S>(identifier: S) -> Self
where
S: Into<Cow<'a, str>>,
{
Self::Identifier(identifier.into())
}
}

impl fmt::Debug for Attr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Int(i) => write!(f, "{i}"),
Self::String(s) => write!(f, "{s:?}"),
Self::Identifier(ident) => write!(f, "{ident}"), // display instead of debug
}
}
}

impl<'a> From<&'a str> for Attr<'a> {
fn from(value: &'a str) -> Self {
Self::String(Cow::Borrowed(value))
}
}

impl From<String> for Attr<'static> {
fn from(value: String) -> Self {
Self::String(Cow::Owned(value))
}
}

impl From<i64> for Attr<'_> {
fn from(value: i64) -> Self {
Self::Int(value)
}
}

#[derive(Default)]
pub struct Attrs<'a>(FxHashMap<Cow<'a, str>, Attr<'a>>);
impl<'a> Attrs<'a> {
#[must_use]
#[inline]
pub fn with<K, V>(mut self, key: K, value: V) -> Self
where
K: Into<Cow<'static, str>>,
V: Into<Attr<'a>>,
{
self += (key, value);
self
}
}

impl<'a, K, V> FromIterator<(K, V)> for Attrs<'a>
where
K: Into<Cow<'static, str>>,
V: Into<Attr<'a>>,
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
Self(iter.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
}
}

impl<'a, K, V> std::ops::AddAssign<(K, V)> for Attrs<'a>
where
K: Into<Cow<'static, str>>,
V: Into<Attr<'a>>,
{
fn add_assign(&mut self, (key, value): (K, V)) {
self.0.insert(key.into(), value.into());
}
}

impl fmt::Debug for Attrs<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.is_empty() {
return Ok(());
}

let l = self.0.len();
for (i, (k, v)) in self.0.iter().enumerate() {
write!(f, "{k}={v:?}")?;
if i < l - 1 {
write!(f, ", ")?;
}
}

Ok(())
}
}
2 changes: 1 addition & 1 deletion crates/oxc_cfg/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod builder;
mod dot;
pub mod dot;
pub mod visit;

use itertools::Itertools;
Expand Down
25 changes: 21 additions & 4 deletions crates/oxc_semantic/src/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use oxc_ast::{
AstKind,
};
use oxc_cfg::{
dot::{Attr, Attrs},
graph::{
dot::{Config, Dot},
visit::EdgeRef,
Expand Down Expand Up @@ -69,14 +70,30 @@ impl DebugDot for ControlFlowGraph {
if !ctx.verbose && matches!(weight, EdgeType::Error(ErrorEdgeKind::Implicit)) {
return String::new();
}
let label = format!("label = \"{weight:?}\" ");
let mut attrs = Attrs::from_iter([("label", format!("{weight:?}"))]);
if matches!(weight, EdgeType::Unreachable)
|| self.basic_block(edge.source()).unreachable
{
format!("{label}, style = \"dotted\" ")
} else {
label
attrs += ("style", "dotted");
}

match weight {
EdgeType::Error(kind) => {
attrs += ("color", Attr::ident("red"));
if matches!(kind, ErrorEdgeKind::Implicit) {
attrs += ("style", Attr::ident("dashed"));
}
}
EdgeType::Backedge => {
attrs += ("color", Attr::ident("grey"));
}
EdgeType::Jump => {
attrs += ("color", Attr::ident("green"));
}
_ => {}
}

format!("{attrs:?}")
},
&|_graph, node| {
let basic_block_index = *node.1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ digraph {
3 [ label = "bb3
ExpressionStatement" shape = box]
4 [ label = "bb4" shape = box]
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 0 [ label = "Error(Implicit)" ]
1 -> 4 [ label = "Normal" ]
1 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 3 [ label="NewFunction"]
4 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 4 [ label="Normal"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ ExpressionStatement" shape = box]
4 [ label = "bb4" shape = box]
5 [ label = "bb5
ExpressionStatement" shape = box]
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
5 -> 4 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "NewFunction" ]
1 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 3 [ label="NewFunction"]
5 -> 4 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 5 [ label="NewFunction"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ ExpressionStatement" shape = box]
7 [ label = "bb7
ExpressionStatement
ExpressionStatement" shape = box]
1 -> 0 [ label = "Error(Implicit)" ]
2 -> 0 [ label = "Error(Implicit)" ]
3 -> 0 [ label = "Error(Implicit)" ]
1 -> 2 [ label = "Normal" ]
1 -> 3 [ label = "Normal" ]
2 -> 3 [ label = "Normal" ]
4 -> 0 [ label = "Error(Implicit)" ]
5 -> 0 [ label = "Error(Implicit)" ]
3 -> 4 [ label = "Normal" ]
3 -> 5 [ label = "Normal" ]
4 -> 5 [ label = "Normal" ]
6 -> 0 [ label = "Error(Implicit)" ]
7 -> 0 [ label = "Error(Implicit)" ]
5 -> 6 [ label = "Normal" ]
5 -> 7 [ label = "Normal" ]
6 -> 7 [ label = "Normal" ]
1 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
2 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 2 [ label="Normal"]
1 -> 3 [ label="Normal"]
2 -> 3 [ label="Normal"]
4 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
5 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 4 [ label="Normal"]
3 -> 5 [ label="Normal"]
4 -> 5 [ label="Normal"]
6 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
7 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
5 -> 6 [ label="Normal"]
5 -> 7 [ label="Normal"]
6 -> 7 [ label="Normal"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ break <A>" shape = box]
2 [ label = "bb2
unreachable" shape = box]
3 [ label = "bb3" shape = box]
1 -> 0 [ label = "Error(Implicit)" ]
2 -> 0 [ label = "Error(Implicit)" , style = "dotted" ]
1 -> 2 [ label = "Unreachable" , style = "dotted" ]
3 -> 0 [ label = "Error(Implicit)" ]
2 -> 3 [ label = "Normal" , style = "dotted" ]
1 -> 3 [ label = "Jump" ]
1 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
2 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 2 [ label="Unreachable", style="dotted"]
3 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
2 -> 3 [ label="Normal", style="dotted"]
1 -> 3 [ label="Jump", color=green]
}
10 changes: 5 additions & 5 deletions crates/oxc_semantic/tests/integration/snapshots/class.snap
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ digraph {
3 [ label = "bb3
ExpressionStatement" shape = box]
4 [ label = "bb4" shape = box]
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 0 [ label = "Error(Implicit)" ]
1 -> 4 [ label = "Normal" ]
1 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 3 [ label="NewFunction"]
4 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 4 [ label="Normal"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ return <value>" shape = box]
4 [ label = "bb4
unreachable" shape = box]
5 [ label = "bb5" shape = box]
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 5 [ label = "Normal" ]
1 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 3 [ label="NewFunction"]
4 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 4 [ label="Unreachable", style="dotted"]
5 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 5 [ label="Normal"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ Condition(CallExpression(a))" shape = box]
5 [ label = "bb5" shape = box]
6 [ label = "bb6" shape = box]
7 [ label = "bb7" shape = box]
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
5 -> 2 [ label = "Error(Implicit)" ]
6 -> 2 [ label = "Error(Implicit)" ]
7 -> 2 [ label = "Error(Implicit)" ]
3 -> 4 [ label = "Normal" ]
5 -> 7 [ label = "Normal" ]
4 -> 5 [ label = "Jump" ]
4 -> 6 [ label = "Normal" ]
6 -> 7 [ label = "Normal" ]
1 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 3 [ label="NewFunction"]
4 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
5 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
6 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
7 -> 2 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 4 [ label="Normal"]
5 -> 7 [ label="Normal"]
4 -> 5 [ label="Jump", color=green]
4 -> 6 [ label="Normal"]
6 -> 7 [ label="Normal"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ Condition(CallExpression(a))" shape = box]
4 [ label = "bb4" shape = box]
5 [ label = "bb5
VariableDeclaration" shape = box]
1 -> 0 [ label = "Error(Implicit)" ]
2 -> 0 [ label = "Error(Implicit)" ]
3 -> 0 [ label = "Error(Implicit)" ]
4 -> 0 [ label = "Error(Implicit)" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 2 [ label = "Normal" ]
3 -> 5 [ label = "Normal" ]
2 -> 3 [ label = "Jump" ]
2 -> 4 [ label = "Normal" ]
4 -> 5 [ label = "Normal" ]
1 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
2 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
3 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
4 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
5 -> 0 [ label="Error(Implicit)", style=dashed, color=red]
1 -> 2 [ label="Normal"]
3 -> 5 [ label="Normal"]
2 -> 3 [ label="Jump", color=green]
2 -> 4 [ label="Normal"]
4 -> 5 [ label="Normal"]
}
Loading