Skip to content

Commit

Permalink
feat(cfg): color-code edges in CFG dot diagrams (#6314)
Browse files Browse the repository at this point in the history
- add `Attrs`, a nominal struct for cleaner management of node/edge styles and
  other attributes
- Style edges in DOT diagrams based on their kind
  • Loading branch information
DonIsaac committed Oct 7, 2024
1 parent f272137 commit 14275b1
Show file tree
Hide file tree
Showing 45 changed files with 661 additions and 538 deletions.
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

0 comments on commit 14275b1

Please sign in to comment.