solang 0.2.1

Solang Solidity Compiler
Documentation
// SPDX-License-Identifier: Apache-2.0

use crate::sema::{
    ast::*,
    symtable::Symtable,
    yul::{
        ast::{YulBlock, YulExpression, YulStatement},
        builtin::YulBuiltInFunction,
    },
};
use solang_parser::{pt, pt::Loc};
use std::fmt::Write;

struct Node {
    name: String,
    labels: Vec<String>,
}

impl Node {
    fn new(name: &str, labels: Vec<String>) -> Self {
        Node {
            name: name.to_owned(),
            labels,
        }
    }
}

struct Edge {
    from: usize,
    to: usize,
    label: Option<String>,
}

struct Dot {
    filename: String,
    nodes: Vec<Node>,
    edges: Vec<Edge>,
}

impl Dot {
    fn write(&self) -> String {
        let mut result = format!("strict digraph \"{}\" {{\n", self.filename);

        for node in &self.nodes {
            if !node.labels.is_empty() {
                writeln!(
                    result,
                    "\t{} [label=\"{}\"]",
                    node.name,
                    node.labels.join("\\n")
                )
                .unwrap();
            }
        }

        for edge in &self.edges {
            if let Some(label) = &edge.label {
                writeln!(
                    result,
                    "\t{} -> {} [label=\"{}\"]",
                    self.nodes[edge.from].name, self.nodes[edge.to].name, label
                )
                .unwrap();
            } else {
                writeln!(
                    result,
                    "\t{} -> {}",
                    self.nodes[edge.from].name, self.nodes[edge.to].name
                )
                .unwrap();
            }
        }

        result.push_str("}\n");

        result
    }

    fn add_node(
        &mut self,
        mut node: Node,
        parent: Option<usize>,
        parent_rel: Option<String>,
    ) -> usize {
        let no = self.nodes.len();

        debug_assert!(
            !node.name.chars().any(|c| c.is_whitespace()),
            "{} contains whitespace",
            node.name
        );

        if node.name.is_empty() || node.name == "node" {
            node.name = format!("node_{}", no);
        } else {
            while self.nodes.iter().any(|n| n.name == node.name) {
                node.name = format!("{}_{}", node.name, no);
            }
        }

        self.nodes.push(node);

        if let Some(parent) = parent {
            self.edges.push(Edge {
                from: parent,
                to: no,
                label: parent_rel,
            })
        }

        no
    }

    fn add_tags(&mut self, tags: &[Tag], parent: usize) {
        if !tags.is_empty() {
            let labels = tags
                .iter()
                .map(|tag| format!("{}: {}", tag.tag, tag.value.to_string().escape_debug()))
                .collect();

            self.add_node(
                Node::new("tags", labels),
                Some(parent),
                Some(String::from("tags")),
            );
        }
    }

    fn add_function(&mut self, func: &Function, ns: &Namespace, parent: usize) {
        let mut labels = vec![
            format!("{} {}", func.ty, func.name),
            ns.loc_to_string(&func.loc),
        ];

        if let Some(contract) = func.contract_no {
            labels.insert(1, format!("contract: {}", ns.contracts[contract].name));
        }

        if func.ty == pt::FunctionTy::Constructor || func.ty == pt::FunctionTy::Function {
            labels.push(format!("signature {}", func.signature));
            labels.push(format!("visibility {}", func.visibility));
        }

        labels.push(format!("mutability {}", func.mutability));

        if func.is_virtual {
            labels.push(String::from("virtual"));
        }

        if let Some((_, is_overrides)) = &func.is_override {
            if is_overrides.is_empty() {
                labels.push(String::from("override"));
            } else {
                for is_override in is_overrides {
                    labels.push(format!("override {}", ns.contracts[*is_override].name));
                }
            }
        }

        if let Some((_, selector)) = &func.selector {
            labels.push(format!("selector {}", hex::encode(selector)));
        }

        let func_node = self.add_node(
            Node::new(&func.name, labels),
            Some(parent),
            Some(format!("{}", func.ty)),
        );

        self.add_tags(&func.tags, func_node);

        // parameters
        if !func.params.is_empty() {
            let mut labels = vec![String::from("parameters")];

            for param in &*func.params {
                labels.push(format!(
                    "{} {}",
                    param.ty.to_string(ns),
                    param.name_as_str()
                ));
            }

            self.add_node(
                Node::new("parameters", labels),
                Some(func_node),
                Some(String::from("parameters")),
            );
        }

        // returns
        if !func.returns.is_empty() {
            let mut labels = vec![String::from("returns")];

            for param in &*func.returns {
                labels.push(format!(
                    "{} {}",
                    param.ty.to_string(ns),
                    param.name_as_str()
                ));
            }

            self.add_node(
                Node::new("returns", labels),
                Some(func_node),
                Some(String::from("returns")),
            );
        }

        // Annotations
        if !func.annotations.is_empty() {
            let node = self.add_node(
                Node::new("annotations", vec!["annotations".into()]),
                Some(func_node),
                Some(String::from("annotations")),
            );

            for note in &func.annotations {
                match note {
                    ConstructorAnnotation::Seed(expr) => {
                        self.add_expression(expr, Some(func), ns, node, "seed".into());
                    }
                    ConstructorAnnotation::Space(expr) => {
                        self.add_expression(expr, Some(func), ns, node, "space".into());
                    }
                    ConstructorAnnotation::Bump(expr) => {
                        self.add_expression(expr, Some(func), ns, node, "bump".into());
                    }
                    ConstructorAnnotation::Payer(expr) => {
                        self.add_expression(expr, Some(func), ns, node, "payer".into());
                    }
                };
            }
        }

        // bases
        for (base_no, (_, _, args)) in &func.bases {
            let node = self.add_node(
                Node::new(
                    &ns.contracts[*base_no].name,
                    vec![ns.contracts[*base_no].name.to_string()],
                ),
                Some(func_node),
                Some(String::from("base")),
            );

            for (no, arg) in args.iter().enumerate() {
                self.add_expression(arg, Some(func), ns, node, format!("arg #{}", no));
            }
        }

        // body
        self.add_statement(&func.body, func, ns, func_node, String::from("body"));
    }

    fn add_expression(
        &mut self,
        expr: &Expression,
        func: Option<&Function>,
        ns: &Namespace,
        parent: usize,
        parent_rel: String,
    ) {
        match expr {
            Expression::BoolLiteral(loc, val) => {
                let labels = vec![
                    format!("bool literal: {}", if *val { "true" } else { "false" }),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("bool_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            Expression::BytesLiteral(loc, ty, val) => {
                let labels = vec![
                    format!("{} literal: {}", ty.to_string(ns), hex::encode(val)),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("bytes_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            Expression::CodeLiteral(loc, contract_no, runtime) => {
                let labels = vec![
                    format!(
                        "code {}literal contract {}",
                        if *runtime { "runtime " } else { "" },
                        ns.contracts[*contract_no].name,
                    ),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("code_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            Expression::NumberLiteral(loc, ty, val) => {
                let labels = vec![
                    format!("{} literal: {}", ty.to_string(ns), val),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("number_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            Expression::RationalNumberLiteral(loc, ty, val) => {
                let labels = vec![
                    format!("rational {} literal: {}", ty.to_string(ns), val),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("rational_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            Expression::StructLiteral(loc, ty, args) => {
                let labels = vec![
                    format!("struct literal: {}", ty.to_string(ns)),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("struct_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                for (no, arg) in args.iter().enumerate() {
                    self.add_expression(arg, func, ns, node, format!("arg #{}", no));
                }
            }
            Expression::ArrayLiteral(loc, ty, _, args) => {
                let labels = vec![
                    format!("array literal: {}", ty.to_string(ns)),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("array_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                for (no, arg) in args.iter().enumerate() {
                    self.add_expression(arg, func, ns, node, format!("arg #{}", no));
                }
            }
            Expression::ConstArrayLiteral(loc, ty, _, args) => {
                let labels = vec![
                    format!("array literal: {}", ty.to_string(ns)),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("array_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                for (no, arg) in args.iter().enumerate() {
                    self.add_expression(arg, func, ns, node, format!("arg #{}", no));
                }
            }
            Expression::Add {
                loc,
                ty,
                unchecked,
                left,
                right,
            } => {
                let mut labels = vec![String::from("add"), ty.to_string(ns), ns.loc_to_string(loc)];
                if *unchecked {
                    labels.push(String::from("unchecked"));
                }
                let node = self.add_node(Node::new("add", labels), Some(parent), Some(parent_rel));

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::Subtract {
                loc,
                ty,
                unchecked,
                left,
                right,
            } => {
                let mut labels = vec![
                    String::from("subtract"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                if *unchecked {
                    labels.push(String::from("unchecked"));
                }
                let node = self.add_node(
                    Node::new("subtract", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::Multiply {
                loc,
                ty,
                unchecked,
                left,
                right,
            } => {
                let mut labels = vec![
                    String::from("multiply"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                if *unchecked {
                    labels.push(String::from("unchecked"));
                }
                let node = self.add_node(
                    Node::new("multiply", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::Divide {
                loc,
                ty,
                left,
                right,
            } => {
                let labels = vec![
                    String::from("divide"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                let node =
                    self.add_node(Node::new("divide", labels), Some(parent), Some(parent_rel));

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::Modulo {
                loc,
                ty,
                left,
                right,
            } => {
                let labels = vec![
                    String::from("modulo"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                let node =
                    self.add_node(Node::new("modulo", labels), Some(parent), Some(parent_rel));

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::Power {
                loc,
                ty,
                unchecked,
                left,
                right,
            } => {
                let mut labels = vec![
                    String::from("power"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                if *unchecked {
                    labels.push(String::from("unchecked"));
                }
                let node =
                    self.add_node(Node::new("power", labels), Some(parent), Some(parent_rel));

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::BitwiseOr {
                loc,
                ty,
                left,
                right,
            } => {
                let labels = vec![
                    String::from("bitwise or"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                let node = self.add_node(
                    Node::new("bitwise_or", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::BitwiseAnd {
                loc,
                ty,
                left,
                right,
            } => {
                let labels = vec![
                    String::from("bitwise and"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                let node = self.add_node(
                    Node::new("bitwise_and", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::BitwiseXor {
                loc,
                ty,
                left,
                right,
            } => {
                let labels = vec![
                    String::from("bitwise xor"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                let node = self.add_node(
                    Node::new("bitwise_xor", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::ShiftLeft {
                loc,
                ty,
                left,
                right,
            } => {
                let labels = vec![
                    String::from("shift left"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                let node = self.add_node(
                    Node::new("shift_left", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::ShiftRight {
                loc,
                ty,
                left,
                right,
                sign: _,
            } => {
                let labels = vec![
                    String::from("shift right"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                let node = self.add_node(
                    Node::new("shift_right", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::ConstantVariable(loc, ty, contract, var_no) => {
                self.add_constant_variable(loc, ty, contract, var_no, parent, parent_rel, ns);
            }
            Expression::Variable(loc, ty, var_no) => {
                let labels = vec![
                    format!("variable: {}", func.unwrap().symtable.vars[var_no].id.name),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("variable", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            Expression::StorageVariable(loc, ty, contract, var_no) => {
                self.add_storage_variable(loc, ty, contract, var_no, parent, parent_rel, ns);
            }
            Expression::Load(loc, ty, expr) => {
                let node = self.add_node(
                    Node::new(
                        "load",
                        vec![format!("load {}", ty.to_string(ns)), ns.loc_to_string(loc)],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::GetRef(loc, ty, expr) => {
                let node = self.add_node(
                    Node::new(
                        "getref",
                        vec![
                            format!("getref {}", ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::StorageLoad(loc, ty, expr) => {
                let node = self.add_node(
                    Node::new(
                        "storage_load",
                        vec![
                            format!("storage load {}", ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::ZeroExt { loc, to, expr } => {
                let node = self.add_node(
                    Node::new(
                        "zero_ext",
                        vec![
                            format!("zero extend {}", to.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::SignExt { loc, to, expr } => {
                let node = self.add_node(
                    Node::new(
                        "sign_ext",
                        vec![
                            format!("sign extend {}", to.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::Trunc { loc, to, expr } => {
                let node = self.add_node(
                    Node::new(
                        "trunc",
                        vec![
                            format!("truncate {}", to.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::CheckingTrunc { loc, to, expr } => {
                let node = self.add_node(
                    Node::new(
                        "trunc",
                        vec![
                            format!("checking truncate {}", to.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::Cast { loc, to, expr } => {
                let node = self.add_node(
                    Node::new(
                        "cast",
                        vec![format!("cast {}", to.to_string(ns)), ns.loc_to_string(loc)],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::BytesCast {
                loc,
                to,
                from,
                expr,
            } => {
                let node = self.add_node(
                    Node::new(
                        "bytes_cast",
                        vec![
                            format!(
                                "bytes cast from {} to {}",
                                from.to_string(ns),
                                to.to_string(ns)
                            ),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::PreIncrement {
                loc,
                ty,
                unchecked,
                expr,
            } => {
                let mut labels = vec![
                    String::from("pre increment"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                if *unchecked {
                    labels.push(String::from("unchecked"));
                }
                let node = self.add_node(
                    Node::new("pre_increment", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::PreDecrement {
                loc,
                ty,
                unchecked,
                expr,
            } => {
                let mut labels = vec![
                    String::from("pre decrement"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                if *unchecked {
                    labels.push(String::from("unchecked"));
                }
                let node = self.add_node(
                    Node::new("pre_decrement", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::PostIncrement {
                loc,
                ty,
                unchecked,
                expr,
            } => {
                let mut labels = vec![
                    String::from("post increment"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                if *unchecked {
                    labels.push(String::from("unchecked"));
                }
                let node = self.add_node(
                    Node::new("post_increment", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::PostDecrement {
                loc,
                ty,
                unchecked,
                expr,
            } => {
                let mut labels = vec![
                    String::from("post decrement"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                if *unchecked {
                    labels.push(String::from("unchecked"));
                }
                let node = self.add_node(
                    Node::new("post_decrement", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::Assign(loc, ty, left, right) => {
                let labels = vec![
                    String::from("assign"),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                let node =
                    self.add_node(Node::new("assign", labels), Some(parent), Some(parent_rel));

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }

            Expression::More(loc, left, right) => {
                let labels = vec![String::from("more"), ns.loc_to_string(loc)];
                let node = self.add_node(Node::new("more", labels), Some(parent), Some(parent_rel));

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::Less(loc, left, right) => {
                let labels = vec![String::from("less"), ns.loc_to_string(loc)];
                let node = self.add_node(Node::new("less", labels), Some(parent), Some(parent_rel));

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::MoreEqual(loc, left, right) => {
                let labels = vec![String::from("more equal"), ns.loc_to_string(loc)];
                let node = self.add_node(
                    Node::new("more_equal", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::LessEqual(loc, left, right) => {
                let labels = vec![String::from("less equal"), ns.loc_to_string(loc)];
                let node = self.add_node(
                    Node::new("less_equal", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::Equal(loc, left, right) => {
                let labels = vec![String::from("equal"), ns.loc_to_string(loc)];
                let node =
                    self.add_node(Node::new("equal", labels), Some(parent), Some(parent_rel));

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::NotEqual(loc, left, right) => {
                let labels = vec![String::from("not equal"), ns.loc_to_string(loc)];
                let node = self.add_node(
                    Node::new("not_qual", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }

            Expression::Not(loc, expr) => {
                let node = self.add_node(
                    Node::new("not", vec![String::from("not"), ns.loc_to_string(loc)]),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::Complement(loc, ty, expr) => {
                let node = self.add_node(
                    Node::new(
                        "complement",
                        vec![
                            format!("complement {}", ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }
            Expression::UnaryMinus(loc, ty, expr) => {
                let node = self.add_node(
                    Node::new(
                        "unary_minus",
                        vec![
                            format!("unary minus {}", ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(expr, func, ns, node, String::from("expr"));
            }

            Expression::ConditionalOperator {
                loc,
                ty,
                cond,
                true_option: left,
                false_option: right,
            } => {
                let node = self.add_node(
                    Node::new(
                        "conditional",
                        vec![
                            format!("conditional operator {}", ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(cond, func, ns, node, String::from("cond"));
                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::Subscript(loc, _, ty, array, index) => {
                let node = self.add_node(
                    Node::new(
                        "subscript",
                        vec![
                            format!("subscript {}", ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(array, func, ns, node, String::from("array"));
                self.add_expression(index, func, ns, node, String::from("index"));
            }
            Expression::StructMember(loc, ty, var, member) => {
                let node = self.add_node(
                    Node::new(
                        "structmember",
                        vec![
                            format!("struct member #{} {}", member, ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(var, func, ns, node, String::from("var"));
            }

            Expression::AllocDynamicBytes(loc, ty, length, initializer) => {
                let mut labels = vec![
                    format!("alloc array {}", ty.to_string(ns)),
                    ns.loc_to_string(loc),
                ];

                if let Some(initializer) = initializer {
                    labels.insert(1, format!("initializer: {}", hex::encode(initializer)));
                }

                let node = self.add_node(
                    Node::new("alloc_array", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(length, func, ns, node, String::from("length"));
            }

            Expression::StorageArrayLength {
                loc,
                ty,
                array,
                elem_ty,
            } => {
                let node = self.add_node(
                    Node::new(
                        "array_length",
                        vec![
                            format!("array length {}", ty.to_string(ns)),
                            format!("element {}", elem_ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(array, func, ns, node, String::from("array"));
            }
            Expression::StringCompare(loc, left, right) => {
                let node = self.add_node(
                    Node::new(
                        "string_cmp",
                        vec![String::from("string compare"), ns.loc_to_string(loc)],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_string_location(left, func, ns, node, String::from("left"));
                self.add_string_location(right, func, ns, node, String::from("right"));
            }
            Expression::StringConcat(loc, ty, left, right) => {
                let node = self.add_node(
                    Node::new(
                        "string_concat",
                        vec![
                            format!("string concat {}", ty.to_string(ns)),
                            ns.loc_to_string(loc),
                        ],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_string_location(left, func, ns, node, String::from("left"));
                self.add_string_location(right, func, ns, node, String::from("right"));
            }

            Expression::Or(loc, left, right) => {
                let labels = vec![String::from("logical or"), ns.loc_to_string(loc)];
                let node = self.add_node(
                    Node::new("logical_or", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }
            Expression::And(loc, left, right) => {
                let labels = vec![String::from("logical and"), ns.loc_to_string(loc)];
                let node = self.add_node(
                    Node::new("logical_and", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(left, func, ns, node, String::from("left"));
                self.add_expression(right, func, ns, node, String::from("right"));
            }

            Expression::InternalFunction {
                loc,
                ty,
                function_no,
                signature,
            } => {
                let mut labels = vec![ty.to_string(ns), ns.loc_to_string(loc)];

                let func = &ns.functions[*function_no];

                if let Some(contract_no) = func.contract_no {
                    labels.insert(
                        1,
                        format!("{}.{}", ns.contracts[contract_no].name, func.name),
                    )
                } else {
                    labels.insert(1, format!("free function {}", func.name))
                }

                if let Some(signature) = signature {
                    labels.insert(1, format!("signature {}", signature))
                }

                self.add_node(
                    Node::new("internal_function", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            Expression::ExternalFunction {
                loc,
                ty,
                function_no,
                address,
            } => {
                let mut labels = vec![ty.to_string(ns), ns.loc_to_string(loc)];

                let f = &ns.functions[*function_no];

                if let Some(contract_no) = f.contract_no {
                    labels.insert(1, format!("{}.{}", ns.contracts[contract_no].name, f.name))
                }

                let node = self.add_node(
                    Node::new("external_function", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(address, func, ns, node, String::from("address"));
            }
            Expression::InternalFunctionCall {
                loc,
                function,
                args,
                ..
            } => {
                let labels = vec![
                    String::from("call internal function"),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("call_internal_function", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(function, func, ns, node, String::from("function"));

                for (no, arg) in args.iter().enumerate() {
                    self.add_expression(arg, func, ns, node, format!("arg #{}", no));
                }
            }
            Expression::ExternalFunctionCall {
                loc,
                function,
                args,
                call_args,
                ..
            } => {
                let labels = vec![
                    String::from("call external function"),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("call_external_function", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(function, func, ns, node, String::from("function"));

                for (no, arg) in args.iter().enumerate() {
                    self.add_expression(arg, func, ns, node, format!("arg #{}", no));
                }

                self.add_call_args(call_args, func, ns, node);
            }
            Expression::ExternalFunctionCallRaw {
                loc,
                address,
                args,
                call_args,
                ..
            } => {
                let labels = vec![
                    String::from("call external function"),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("call_external_function", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_expression(address, func, ns, node, String::from("address"));
                self.add_expression(args, func, ns, node, String::from("args"));
                self.add_call_args(call_args, func, ns, node);
            }
            Expression::Constructor {
                loc,
                contract_no,
                args,
                call_args,
                ..
            } => {
                let labels = vec![
                    format!("constructor contract {}", ns.contracts[*contract_no].name),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("constructor", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                for (no, arg) in args.iter().enumerate() {
                    self.add_expression(arg, func, ns, node, format!("arg #{}", no));
                }

                self.add_call_args(call_args, func, ns, node);
            }

            Expression::FormatString(loc, args) => {
                let labels = vec![String::from("string format"), ns.loc_to_string(loc)];

                let node = self.add_node(
                    Node::new("string_format", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                for (no, (_, arg)) in args.iter().enumerate() {
                    self.add_expression(arg, func, ns, node, format!("arg #{}", no));
                }
            }
            Expression::Builtin(loc, _, builtin, args) => {
                let labels = vec![format!("builtin {:?}", builtin), ns.loc_to_string(loc)];

                let node = self.add_node(
                    Node::new("builtins", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                for (no, arg) in args.iter().enumerate() {
                    self.add_expression(arg, func, ns, node, format!("arg #{}", no));
                }
            }
            Expression::InterfaceId(loc, contract_no) => {
                let labels = vec![
                    format!("interfaceid contract {}", ns.contracts[*contract_no].name),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("interfaceid", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            Expression::List(loc, list) => {
                let labels = vec![String::from("list"), ns.loc_to_string(loc)];

                let node = self.add_node(Node::new("list", labels), Some(parent), Some(parent_rel));

                for (no, expr) in list.iter().enumerate() {
                    self.add_expression(expr, func, ns, node, format!("entry #{}", no));
                }
            }
        }
    }

    fn add_call_args(
        &mut self,
        call_args: &CallArgs,
        func: Option<&Function>,
        ns: &Namespace,
        node: usize,
    ) {
        if let Some(gas) = &call_args.gas {
            self.add_expression(gas, func, ns, node, String::from("gas"));
        }
        if let Some(value) = &call_args.value {
            self.add_expression(value, func, ns, node, String::from("value"));
        }
        if let Some(salt) = &call_args.salt {
            self.add_expression(salt, func, ns, node, String::from("salt"));
        }
        if let Some(address) = &call_args.address {
            self.add_expression(address, func, ns, node, String::from("address"));
        }
        if let Some(accounts) = &call_args.accounts {
            self.add_expression(accounts, func, ns, node, String::from("accounts"));
        }
        if let Some(seeds) = &call_args.seeds {
            self.add_expression(seeds, func, ns, node, String::from("seeds"));
        }
    }

    fn add_string_location(
        &mut self,
        loc: &StringLocation<Expression>,
        func: Option<&Function>,
        ns: &Namespace,
        parent: usize,
        parent_rel: String,
    ) {
        match loc {
            StringLocation::CompileTime(val) => {
                self.add_node(
                    Node::new(
                        "compile_time_string",
                        vec![format!("const string {}", hex::encode(val))],
                    ),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            StringLocation::RunTime(expr) => {
                self.add_expression(expr, func, ns, parent, parent_rel);
            }
        }
    }

    fn add_statement(
        &mut self,
        stmts: &[Statement],
        func: &Function,
        ns: &Namespace,
        parent: usize,
        parent_rel: String,
    ) {
        let mut parent = parent;
        let mut parent_rel = parent_rel;

        for stmt in stmts {
            match stmt {
                Statement::Block {
                    loc,
                    unchecked,
                    statements,
                } => {
                    let mut labels = vec![String::from("block"), ns.loc_to_string(loc)];

                    if *unchecked {
                        labels.push(String::from("unchecked"));
                    }

                    parent =
                        self.add_node(Node::new("block", labels), Some(parent), Some(parent_rel));

                    self.add_statement(statements, func, ns, parent, String::from("statements"));
                }
                Statement::VariableDecl(loc, _, param, init) => {
                    let labels = vec![
                        format!(
                            "variable decl {} {}",
                            param.ty.to_string(ns),
                            param.name_as_str()
                        ),
                        ns.loc_to_string(loc),
                    ];

                    parent = self.add_node(
                        Node::new("var_decl", labels),
                        Some(parent),
                        Some(parent_rel),
                    );

                    if let Some(init) = init {
                        self.add_expression(init, Some(func), ns, parent, String::from("init"));
                    }
                }
                Statement::If(loc, _, cond, then, else_) => {
                    let labels = vec![String::from("if"), ns.loc_to_string(loc)];

                    parent = self.add_node(Node::new("if", labels), Some(parent), Some(parent_rel));

                    self.add_expression(cond, Some(func), ns, parent, String::from("cond"));
                    self.add_statement(then, func, ns, parent, String::from("then"));
                    self.add_statement(else_, func, ns, parent, String::from("else"));
                }
                Statement::While(loc, _, cond, body) => {
                    let labels = vec![String::from("while"), ns.loc_to_string(loc)];

                    parent =
                        self.add_node(Node::new("while", labels), Some(parent), Some(parent_rel));

                    self.add_expression(cond, Some(func), ns, parent, String::from("cond"));
                    self.add_statement(body, func, ns, parent, String::from("body"));
                }
                Statement::For {
                    loc,
                    init,
                    cond,
                    next,
                    body,
                    ..
                } => {
                    let labels = vec![String::from("for"), ns.loc_to_string(loc)];

                    parent =
                        self.add_node(Node::new("for", labels), Some(parent), Some(parent_rel));

                    self.add_statement(init, func, ns, parent, String::from("init"));
                    if let Some(cond) = cond {
                        self.add_expression(cond, Some(func), ns, parent, String::from("cond"));
                    }
                    self.add_statement(next, func, ns, parent, String::from("next"));
                    self.add_statement(body, func, ns, parent, String::from("body"));
                }
                Statement::DoWhile(loc, _, body, cond) => {
                    let labels = vec![String::from("do while"), ns.loc_to_string(loc)];

                    parent =
                        self.add_node(Node::new("dowhile", labels), Some(parent), Some(parent_rel));

                    self.add_statement(body, func, ns, parent, String::from("body"));
                    self.add_expression(cond, Some(func), ns, parent, String::from("cond"));
                }
                Statement::Expression(loc, _, expr) => {
                    let labels = vec![String::from("expression"), ns.loc_to_string(loc)];

                    parent =
                        self.add_node(Node::new("expr", labels), Some(parent), Some(parent_rel));

                    self.add_expression(expr, Some(func), ns, parent, String::from("expr"));
                }
                Statement::Delete(loc, ty, expr) => {
                    let labels = vec![
                        String::from("delete"),
                        format!("ty: {}", ty.to_string(ns)),
                        ns.loc_to_string(loc),
                    ];

                    parent =
                        self.add_node(Node::new("delete", labels), Some(parent), Some(parent_rel));

                    self.add_expression(expr, Some(func), ns, parent, String::from("expr"));
                }
                Statement::Destructure(loc, fields, expr) => {
                    let labels = vec![String::from("destructure"), ns.loc_to_string(loc)];

                    parent = self.add_node(
                        Node::new("destructure", labels),
                        Some(parent),
                        Some(parent_rel),
                    );

                    for (no, field) in fields.iter().enumerate() {
                        let parent_rel = format!("arg #{}", no);

                        match field {
                            DestructureField::None => {
                                self.add_node(
                                    Node::new("none", vec![String::from("none")]),
                                    Some(parent),
                                    Some(parent_rel),
                                );
                            }
                            DestructureField::Expression(expr) => {
                                self.add_expression(expr, Some(func), ns, parent, parent_rel);
                            }
                            DestructureField::VariableDecl(_, param) => {
                                self.add_node(
                                    Node::new(
                                        "param",
                                        vec![format!(
                                            "{} {}",
                                            param.ty.to_string(ns),
                                            param.name_as_str()
                                        )],
                                    ),
                                    Some(parent),
                                    Some(parent_rel),
                                );
                            }
                        }
                    }

                    self.add_expression(expr, Some(func), ns, parent, String::from("expr"));
                }
                Statement::Continue(loc) => {
                    let labels = vec![String::from("continue"), ns.loc_to_string(loc)];

                    parent = self.add_node(
                        Node::new("continue", labels),
                        Some(parent),
                        Some(parent_rel),
                    );
                }
                Statement::Break(loc) => {
                    let labels = vec![String::from("break"), ns.loc_to_string(loc)];

                    parent =
                        self.add_node(Node::new("break", labels), Some(parent), Some(parent_rel));
                }
                Statement::Return(loc, expr) => {
                    let labels = vec![String::from("return"), ns.loc_to_string(loc)];

                    parent =
                        self.add_node(Node::new("return", labels), Some(parent), Some(parent_rel));

                    if let Some(expr) = expr {
                        self.add_expression(expr, Some(func), ns, parent, String::from("expr"));
                    }
                }
                Statement::Emit {
                    loc,
                    event_no,
                    args,
                    ..
                } => {
                    let mut labels = vec![String::from("emit"), ns.loc_to_string(loc)];

                    let event = &ns.events[*event_no];

                    if let Some(contract) = event.contract {
                        labels.insert(
                            1,
                            format!("event {}.{}", ns.contracts[contract].name, event.name),
                        );
                    } else {
                        labels.insert(1, format!("event {}", event.name));
                    }

                    parent =
                        self.add_node(Node::new("emit", labels), Some(parent), Some(parent_rel));

                    for (no, arg) in args.iter().enumerate() {
                        self.add_expression(arg, Some(func), ns, parent, format!("arg #{}", no));
                    }
                }
                Statement::TryCatch(loc, _, try_catch) => {
                    let labels = vec![String::from("try"), ns.loc_to_string(loc)];

                    self.add_expression(
                        &try_catch.expr,
                        Some(func),
                        ns,
                        parent,
                        String::from("expr"),
                    );

                    parent =
                        self.add_node(Node::new("try", labels), Some(parent), Some(parent_rel));

                    for (no, (_, param)) in try_catch.returns.iter().enumerate() {
                        let parent_rel = format!("return #{}", no);

                        self.add_node(
                            Node::new(
                                "return",
                                vec![format!(
                                    "{} {}",
                                    param.ty.to_string(ns),
                                    param.name_as_str()
                                )],
                            ),
                            Some(parent),
                            Some(parent_rel),
                        );
                    }

                    self.add_statement(&try_catch.ok_stmt, func, ns, parent, String::from("ok"));

                    for (_, param, stmt) in &try_catch.errors {
                        self.add_node(
                            Node::new(
                                "error_param",
                                vec![format!(
                                    "{} {}",
                                    param.ty.to_string(ns),
                                    param.name_as_str()
                                )],
                            ),
                            Some(parent),
                            Some(String::from("error parameter")),
                        );

                        self.add_statement(stmt, func, ns, parent, String::from("error"));
                    }

                    if let Some(param) = &try_catch.catch_param {
                        self.add_node(
                            Node::new(
                                "catch_param",
                                vec![format!(
                                    "{} {}",
                                    param.ty.to_string(ns),
                                    param.name_as_str()
                                )],
                            ),
                            Some(parent),
                            Some(String::from("catch parameter")),
                        );
                    }

                    self.add_statement(
                        &try_catch.catch_stmt,
                        func,
                        ns,
                        parent,
                        String::from("catch"),
                    );
                }
                Statement::Underscore(loc) => {
                    let labels = vec![String::from("undersore"), ns.loc_to_string(loc)];

                    parent = self.add_node(
                        Node::new("underscore", labels),
                        Some(parent),
                        Some(parent_rel),
                    );
                }
                Statement::Assembly(inline_assembly, ..) => {
                    let labels = vec![
                        "inline assembly".to_string(),
                        ns.loc_to_string(&inline_assembly.loc),
                    ];
                    parent = self.add_node(
                        Node::new("inline_assembly", labels),
                        Some(parent),
                        Some(parent_rel),
                    );

                    let mut local_parent = parent;

                    for n in inline_assembly.functions.start..inline_assembly.functions.end {
                        self.add_yul_function(
                            n,
                            ns,
                            local_parent,
                            format!("func def #{}", inline_assembly.functions.end - n),
                        );
                    }

                    local_parent = parent;
                    for (item_no, item) in inline_assembly.body.iter().enumerate() {
                        local_parent = self.add_yul_statement(
                            item,
                            local_parent,
                            format!("statement #{}", item_no),
                            &func.symtable,
                            ns,
                        );
                    }
                }
            }
            parent_rel = String::from("next");
        }
    }

    fn add_yul_function(
        &mut self,
        func_no: usize,
        ns: &Namespace,
        parent: usize,
        parent_rel: String,
    ) {
        let labels = vec![
            format!("function definition {}", ns.yul_functions[func_no].name),
            ns.loc_to_string(&ns.yul_functions[func_no].loc),
        ];

        let func_node = self.add_node(
            Node::new("yul_function_definition", labels),
            Some(parent),
            Some(parent_rel),
        );

        let mut local_parent = func_node;
        for (item_no, item) in (*ns.yul_functions[func_no].params).iter().enumerate() {
            let labels = vec![
                format!(
                    "function parameter {}: {}",
                    item.ty.to_string(ns),
                    item.id.as_ref().unwrap().name,
                ),
                ns.loc_to_string(&item.loc),
            ];
            local_parent = self.add_node(
                Node::new("yul_function_parameter", labels),
                Some(local_parent),
                Some(format!("parameter #{}", item_no)),
            );
        }

        local_parent = func_node;
        for (item_no, item) in (*ns.yul_functions[func_no].returns).iter().enumerate() {
            let labels = vec![
                format!(
                    "return parameter {}: {}",
                    item.ty.to_string(ns),
                    item.id.as_ref().unwrap().name
                ),
                ns.loc_to_string(&item.loc),
            ];
            local_parent = self.add_node(
                Node::new("yul_function_return", labels),
                Some(local_parent),
                Some(format!("return #{}", item_no)),
            );
        }

        local_parent = func_node;
        for (item_no, item) in ns.yul_functions[func_no].body.iter().enumerate() {
            local_parent = self.add_yul_statement(
                item,
                local_parent,
                format!("statement #{}", item_no),
                &ns.yul_functions[func_no].symtable,
                ns,
            );
        }
    }

    fn add_yul_expression(
        &mut self,
        expr: &YulExpression,
        symtable: &Symtable,
        ns: &Namespace,
        parent: usize,
        parent_rel: String,
    ) {
        match expr {
            YulExpression::BoolLiteral(loc, value, ty) => {
                let labels = vec![
                    format!(
                        "bool literal: {} of type {}",
                        if *value { "true" } else { "false" },
                        ty.to_string(ns)
                    ),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("yul_bool_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            YulExpression::NumberLiteral(loc, value, ty) => {
                let labels = vec![
                    format!("{} literal: {}", ty.to_string(ns), value),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("yul_number_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            YulExpression::StringLiteral(loc, value, ty) => {
                let labels = vec![
                    format!("{} literal: {}", ty.to_string(ns), hex::encode(value)),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("bytes_literal", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            YulExpression::YulLocalVariable(loc, ty, var_no) => {
                let labels = vec![
                    format!("yul variable: {}", symtable.vars[var_no].id.name),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];
                self.add_node(
                    Node::new("yul_variable", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            YulExpression::SolidityLocalVariable(loc, ty, _, var_no) => {
                let labels = vec![
                    format!("solidity variable: {}", symtable.vars[var_no].id.name),
                    ty.to_string(ns),
                    ns.loc_to_string(loc),
                ];

                self.add_node(
                    Node::new("solidity_variable", labels),
                    Some(parent),
                    Some(parent_rel),
                );
            }
            YulExpression::ConstantVariable(loc, ty, contract, var_no) => {
                self.add_constant_variable(loc, ty, contract, var_no, parent, parent_rel, ns);
            }
            YulExpression::StorageVariable(loc, ty, contract, var_no) => {
                self.add_storage_variable(loc, ty, contract, var_no, parent, parent_rel, ns);
            }
            YulExpression::BuiltInCall(loc, builtin_ty, args) => {
                self.add_yul_builtin_call(loc, builtin_ty, args, parent, parent_rel, symtable, ns);
            }
            YulExpression::FunctionCall(loc, func_no, args, _) => {
                self.add_yul_function_call(loc, func_no, args, parent, parent_rel, symtable, ns);
            }
            YulExpression::SuffixAccess(loc, member, suffix) => {
                let labels = vec![
                    format!("yul suffix '{}' access", suffix.to_string()),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("yul_suffix_access", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                self.add_yul_expression(member, symtable, ns, node, "parent".to_string());
            }
        }
    }

    fn add_constant_variable(
        &mut self,
        loc: &Loc,
        ty: &Type,
        contract: &Option<usize>,
        var_no: &usize,
        parent: usize,
        parent_rel: String,
        ns: &Namespace,
    ) {
        let mut labels = vec![
            String::from("constant variable"),
            ty.to_string(ns),
            ns.loc_to_string(loc),
        ];

        if let Some(contract) = contract {
            labels.insert(
                1,
                format!(
                    "{}.{}",
                    ns.contracts[*contract].name, ns.contracts[*contract].variables[*var_no].name
                ),
            );
        } else {
            labels.insert(1, ns.constants[*var_no].name.to_string());
        }

        self.add_node(
            Node::new("constant", labels),
            Some(parent),
            Some(parent_rel),
        );
    }

    fn add_storage_variable(
        &mut self,
        loc: &Loc,
        ty: &Type,
        contract: &usize,
        var_no: &usize,
        parent: usize,
        parent_rel: String,
        ns: &Namespace,
    ) {
        let labels = vec![
            String::from("storage variable"),
            format!(
                "{}.{}",
                ns.contracts[*contract].name, ns.contracts[*contract].variables[*var_no].name
            ),
            ty.to_string(ns),
            ns.loc_to_string(loc),
        ];

        self.add_node(
            Node::new("storage_var", labels),
            Some(parent),
            Some(parent_rel),
        );
    }

    fn add_yul_statement(
        &mut self,
        statement: &YulStatement,
        parent: usize,
        parent_rel: String,
        symtable: &Symtable,
        ns: &Namespace,
    ) -> usize {
        match statement {
            YulStatement::FunctionCall(loc, _, func_no, args) => {
                self.add_yul_function_call(loc, func_no, args, parent, parent_rel, symtable, ns)
            }
            YulStatement::BuiltInCall(loc, _, builtin_ty, args) => {
                self.add_yul_builtin_call(loc, builtin_ty, args, parent, parent_rel, symtable, ns)
            }
            YulStatement::Block(block) => {
                self.add_yul_block(block, parent, parent_rel, symtable, ns)
            }
            YulStatement::VariableDeclaration(loc, _, declared_vars, initializer) => {
                let labels = vec![
                    "yul variable declaration".to_string(),
                    ns.loc_to_string(loc),
                ];

                let node = self.add_node(
                    Node::new("yul_var_decl", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                for (decl_no, item) in declared_vars.iter().enumerate() {
                    let var = &symtable.vars[&item.0];
                    self.add_node(
                        Node::new(
                            "var_decl_item",
                            vec![
                                format!(
                                    "yul variable declaration {} {}",
                                    var.ty.to_string(ns),
                                    var.id.name
                                ),
                                ns.loc_to_string(&var.id.loc),
                            ],
                        ),
                        Some(node),
                        Some(format!("decl item #{}", decl_no)),
                    );
                }

                if let Some(init) = initializer {
                    self.add_yul_expression(init, symtable, ns, node, "init".to_string());
                }

                node
            }
            YulStatement::Assignment(loc, _, lhs, rhs) => {
                let labels = vec!["yul assignment".to_string(), ns.loc_to_string(loc)];

                let node = self.add_node(
                    Node::new("yul_assignment", labels),
                    Some(parent),
                    Some(parent_rel),
                );

                for (item_no, item) in lhs.iter().enumerate() {
                    self.add_yul_expression(item, symtable, ns, node, format!("rhs #{}", item_no));
                }

                self.add_yul_expression(rhs, symtable, ns, node, "lhs".to_string());
                node
            }
            YulStatement::IfBlock(loc, _, condition, block) => {
                let labels = vec!["yul if".to_string(), ns.loc_to_string(loc)];

                let node = self.add_node(Node::new("if", labels), Some(parent), Some(parent_rel));

                self.add_yul_expression(condition, symtable, ns, node, "cond".to_string());
                self.add_yul_block(block, node, "if-block".to_string(), symtable, ns);
                node
            }
            YulStatement::Switch {
                loc,
                condition,
                cases,
                default,
                ..
            } => {
                let labels = vec!["yul switch".to_string(), ns.loc_to_string(loc)];

                let node =
                    self.add_node(Node::new("switch", labels), Some(parent), Some(parent_rel));

                self.add_yul_expression(condition, symtable, ns, node, "cond".to_string());

                for (item_no, item) in cases.iter().enumerate() {
                    let case_block = self.add_node(
                        Node::new(
                            "case",
                            vec!["yul switch case".to_string(), ns.loc_to_string(&item.loc)],
                        ),
                        Some(node),
                        Some(format!("case #{}", item_no)),
                    );
                    self.add_yul_expression(
                        &item.condition,
                        symtable,
                        ns,
                        case_block,
                        "case-condition".to_string(),
                    );
                    self.add_yul_block(
                        &item.block,
                        case_block,
                        "case block".to_string(),
                        symtable,
                        ns,
                    );
                }

                if let Some(default_block) = default {
                    let default_node = self.add_node(
                        Node::new(
                            "default",
                            vec![
                                "yul switch default".to_string(),
                                ns.loc_to_string(&default_block.loc),
                            ],
                        ),
                        Some(node),
                        Some("default".to_string()),
                    );
                    self.add_yul_block(
                        default_block,
                        default_node,
                        "default block".to_string(),
                        symtable,
                        ns,
                    );
                }
                node
            }
            YulStatement::For {
                loc,
                init_block,
                condition,
                post_block,
                execution_block,
                ..
            } => {
                let labels = vec!["yul for".to_string(), ns.loc_to_string(loc)];

                let node = self.add_node(Node::new("for", labels), Some(parent), Some(parent_rel));

                self.add_yul_block(init_block, node, "init block".to_string(), symtable, ns);
                self.add_yul_expression(condition, symtable, ns, node, "for condition".to_string());
                self.add_yul_block(post_block, node, "post block".to_string(), symtable, ns);
                self.add_yul_block(
                    execution_block,
                    node,
                    "execution block".to_string(),
                    symtable,
                    ns,
                );
                node
            }
            YulStatement::Leave(loc, _) => {
                let labels = vec!["leave".to_string(), ns.loc_to_string(loc)];
                self.add_node(Node::new("leave", labels), Some(parent), Some(parent_rel))
            }
            YulStatement::Break(loc, _) => {
                let labels = vec!["break".to_string(), ns.loc_to_string(loc)];
                self.add_node(Node::new("break", labels), Some(parent), Some(parent_rel))
            }
            YulStatement::Continue(loc, _) => {
                let labels = vec!["continue".to_string(), ns.loc_to_string(loc)];
                self.add_node(
                    Node::new("continue", labels),
                    Some(parent),
                    Some(parent_rel),
                )
            }
        }
    }

    fn add_yul_block(
        &mut self,
        block: &YulBlock,
        mut parent: usize,
        parent_rel: String,
        symtable: &Symtable,
        ns: &Namespace,
    ) -> usize {
        let label = vec!["assembly block".to_string(), ns.loc_to_string(&block.loc)];

        let node = self.add_node(
            Node::new("assembly_block", label),
            Some(parent),
            Some(parent_rel),
        );

        parent = node;
        for (statement_no, child_statement) in block.body.iter().enumerate() {
            parent = self.add_yul_statement(
                child_statement,
                parent,
                format!("statement #{}", statement_no),
                symtable,
                ns,
            );
        }

        node
    }

    fn add_yul_function_call(
        &mut self,
        loc: &Loc,
        func_no: &usize,
        args: &[YulExpression],
        parent: usize,
        parent_rel: String,
        symtable: &Symtable,
        ns: &Namespace,
    ) -> usize {
        let labels = vec![
            format!("yul function call '{}'", ns.yul_functions[*func_no].name),
            ns.loc_to_string(loc),
        ];

        let node = self.add_node(
            Node::new("yul_function_call", labels),
            Some(parent),
            Some(parent_rel),
        );

        for (arg_no, arg) in args.iter().enumerate() {
            self.add_yul_expression(arg, symtable, ns, node, format!("arg #{}", arg_no));
        }

        node
    }

    fn add_yul_builtin_call(
        &mut self,
        loc: &Loc,
        builtin_ty: &YulBuiltInFunction,
        args: &[YulExpression],
        parent: usize,
        parent_rel: String,
        symtable: &Symtable,
        ns: &Namespace,
    ) -> usize {
        let labels = vec![
            format!("yul builtin call '{}'", builtin_ty.to_string()),
            ns.loc_to_string(loc),
        ];

        let node = self.add_node(
            Node::new("yul_builtin_call", labels),
            Some(parent),
            Some(parent_rel),
        );

        for (arg_no, arg) in args.iter().enumerate() {
            self.add_yul_expression(arg, symtable, ns, node, format!("arg #{}", arg_no));
        }

        node
    }
}

impl Namespace {
    pub fn dotgraphviz(&self) -> String {
        let mut dot = Dot {
            filename: format!("{}", self.files[self.top_file_no()].path.display()),
            nodes: Vec::new(),
            edges: Vec::new(),
        };

        // enums
        if !self.enums.is_empty() {
            let enums = dot.add_node(Node::new("enums", Vec::new()), None, None);

            for decl in &self.enums {
                let mut labels = decl
                    .values
                    .iter()
                    .map(|(name, _)| format!("value: {}", name))
                    .collect::<Vec<String>>();

                labels.insert(0, self.loc_to_string(&decl.loc));
                if let Some(contract) = &decl.contract {
                    labels.insert(0, format!("contract: {}", contract));
                }
                labels.insert(0, format!("name: {}", decl.name));

                let e = Node::new(&decl.name, labels);

                let node = dot.add_node(e, Some(enums), None);

                dot.add_tags(&decl.tags, node);
            }
        }

        // structs
        if !self.structs.is_empty() {
            let structs = dot.add_node(Node::new("structs", Vec::new()), None, None);

            for decl in &self.structs {
                if let pt::Loc::File(..) = &decl.loc {
                    let mut labels =
                        vec![format!("name:{}", decl.name), self.loc_to_string(&decl.loc)];

                    if let Some(contract) = &decl.contract {
                        labels.insert(1, format!("contract: {}", contract));
                    }

                    for field in &decl.fields {
                        labels.push(format!(
                            "field name:{} ty:{}",
                            field.name_as_str(),
                            field.ty.to_string(self)
                        ));
                    }

                    let e = Node::new(&decl.name, labels);

                    let node = dot.add_node(e, Some(structs), None);

                    dot.add_tags(&decl.tags, node);
                }
            }
        }

        // events
        if !self.events.is_empty() {
            let events = dot.add_node(Node::new("events", Vec::new()), None, None);

            for decl in &self.events {
                let mut labels = vec![format!("name:{}", decl.name), self.loc_to_string(&decl.loc)];

                if let Some(contract) = &decl.contract {
                    labels.insert(1, format!("contract: {}", contract));
                }

                if decl.anonymous {
                    labels.push(String::from("anonymous"));
                }

                for field in &decl.fields {
                    labels.push(format!(
                        "field name:{} ty:{} indexed:{}",
                        field.name_as_str(),
                        field.ty.to_string(self),
                        if field.indexed { "yes" } else { "no" }
                    ));
                }

                let e = Node::new(&decl.name, labels);

                let node = dot.add_node(e, Some(events), None);

                dot.add_tags(&decl.tags, node);
            }
        }

        // user types
        if self.user_types.iter().any(|t| t.loc != pt::Loc::Builtin) {
            let types = dot.add_node(Node::new("types", Vec::new()), None, None);

            for decl in self.user_types.iter().filter(|t| t.loc != pt::Loc::Builtin) {
                let mut labels = vec![
                    format!("name:{} ty:{}", decl.name, decl.ty.to_string(self)),
                    self.loc_to_string(&decl.loc),
                ];

                if let Some(contract) = &decl.contract {
                    labels.insert(1, format!("contract: {}", contract));
                }

                let e = Node::new(&decl.name, labels);

                let node = dot.add_node(e, Some(types), None);

                dot.add_tags(&decl.tags, node);
            }
        }

        // free functions
        if self
            .functions
            .iter()
            .any(|func| func.contract_no.is_none() && func.loc != pt::Loc::Builtin)
        {
            let functions = dot.add_node(Node::new("free_functions", Vec::new()), None, None);

            for func in &self.functions {
                if func.contract_no.is_none() && func.loc != pt::Loc::Builtin {
                    dot.add_function(func, self, functions);
                }
            }
        }

        let contracts = dot.add_node(Node::new("contracts", Vec::new()), None, None);

        // contracts
        for c in &self.contracts {
            let contract = dot.add_node(
                Node::new(
                    "contract",
                    vec![format!("contract {}", c.name), self.loc_to_string(&c.loc)],
                ),
                Some(contracts),
                None,
            );

            dot.add_tags(&c.tags, contract);

            for base in &c.bases {
                let node = dot.add_node(
                    Node::new(
                        "base",
                        vec![
                            format!("base {}", self.contracts[base.contract_no].name),
                            self.loc_to_string(&base.loc),
                        ],
                    ),
                    Some(contract),
                    Some(String::from("base")),
                );

                if let Some((_, args)) = &base.constructor {
                    for (no, arg) in args.iter().enumerate() {
                        dot.add_expression(arg, None, self, node, format!("arg #{}", no));
                    }
                }
            }

            for var in &c.variables {
                let mut labels = vec![
                    format!("variable {}", var.name),
                    format!("visibility {}", var.visibility),
                    self.loc_to_string(&var.loc),
                ];

                if var.immutable {
                    labels.insert(2, String::from("immutable"));
                }

                if var.constant {
                    labels.insert(2, String::from("constant"));
                }

                let node = dot.add_node(
                    Node::new("var", labels),
                    Some(contract),
                    Some(String::from("variable")),
                );

                if let Some(initializer) = &var.initializer {
                    dot.add_expression(initializer, None, self, node, String::from("initializer"));
                }

                dot.add_tags(&var.tags, node);
            }

            for using in &c.using {
                let mut labels = match &using.list {
                    UsingList::Functions(functions) => functions
                        .iter()
                        .map(|func_no| {
                            let func = &self.functions[*func_no];

                            format!("function {} {}", func.name, self.loc_to_string(&func.loc))
                        })
                        .collect(),
                    UsingList::Library(library_no) => {
                        let library = &self.contracts[*library_no];

                        vec![format!("library {}", library.name)]
                    }
                };

                if let Some(ty) = &using.ty {
                    labels.insert(0, format!("using for {}", ty.to_string(self)));
                }

                dot.add_node(
                    Node::new("using", labels),
                    Some(contract),
                    Some(String::from("base")),
                );
            }

            for func in &c.functions {
                dot.add_function(&self.functions[*func], self, contract);
            }
        }

        // diagnostics
        if !self.diagnostics.is_empty() {
            let diagnostics = dot.add_node(Node::new("diagnostics", Vec::new()), None, None);

            for diag in self.diagnostics.iter() {
                let mut labels = vec![
                    diag.message.to_string().replace('"', "\\\""),
                    format!("level {:?}", diag.level),
                ];

                labels.push(self.loc_to_string(&diag.loc));

                let node = dot.add_node(
                    Node::new("diagnostic", labels),
                    Some(diagnostics),
                    Some(format!("{:?}", diag.level)),
                );

                for note in &diag.notes {
                    dot.add_node(
                        Node::new(
                            "note",
                            vec![note.message.to_string(), self.loc_to_string(&note.loc)],
                        ),
                        Some(node),
                        Some(String::from("note")),
                    );
                }
            }
        }

        dot.write()
    }
}