diff --git a/bin/fuzz.rs b/bin/fuzz.rs index a86ca93..57cf325 100644 --- a/bin/fuzz.rs +++ b/bin/fuzz.rs @@ -1,7 +1,7 @@ -use clap::Parser; - use std::path::Path; +use clap::Parser; + #[derive(Debug, Parser)] #[clap(name = "fuzz", version, author, about)] struct FuzzCli { diff --git a/bin/kecc.rs b/bin/kecc.rs index 3b313fa..e534ca9 100644 --- a/bin/kecc.rs +++ b/bin/kecc.rs @@ -1,18 +1,16 @@ -use clap::Parser; - use std::ffi::OsStr; use std::io::Write; use std::os::unix::io::{FromRawFd, IntoRawFd}; use std::path::Path; use std::process::{Command, Stdio}; -use lang_c::ast::TranslationUnit; -use tempfile::tempdir; - +use clap::Parser; use kecc::{ ir, ok_or_exit, write, Asmgen, Deadcode, Gvn, IrParse, IrVisualizer, Irgen, Mem2reg, Optimize, Parse, SimplifyCfg, Translate, O1, }; +use lang_c::ast::TranslationUnit; +use tempfile::tempdir; #[derive(Debug, Parser)] #[clap(name = "kecc", version, author, about)] diff --git a/src/asm/mod.rs b/src/asm/mod.rs index 7855c14..f3eef13 100644 --- a/src/asm/mod.rs +++ b/src/asm/mod.rs @@ -1,10 +1,10 @@ mod write_asm; -use crate::ir; - use core::convert::TryFrom; use core::fmt; +use crate::ir; + /// An assembly file. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Asm { diff --git a/src/asmgen/mod.rs b/src/asmgen/mod.rs index 121b42c..b28c71f 100644 --- a/src/asmgen/mod.rs +++ b/src/asmgen/mod.rs @@ -1,15 +1,24 @@ -use crate::asm; -use crate::ir; -use crate::Translate; +use std::collections::{HashMap, HashSet, VecDeque}; -#[derive(Default, Clone, Copy, Debug)] +use lang_c::ast; + +use crate::ir::HasDtype; +use crate::{asm, ir, Translate}; + +#[derive(Debug)] pub struct Asmgen {} +impl Default for Asmgen { + fn default() -> Self { + todo!() + } +} + impl Translate for Asmgen { type Target = asm::Asm; type Error = (); - fn translate(&mut self, _source: &ir::TranslationUnit) -> Result { - todo!("Homework: Assembly Generation") + fn translate(&mut self, source: &ir::TranslationUnit) -> Result { + todo!() } } diff --git a/src/c/parse.rs b/src/c/parse.rs index b38e998..16e4550 100644 --- a/src/c/parse.rs +++ b/src/c/parse.rs @@ -8,14 +8,14 @@ use lang_c::span::Node; use crate::utils::AssertSupported; use crate::Translate; -/// TODO(document) +/// Parse Error #[derive(Debug)] pub enum Error { ParseError(ParseError), Unsupported, } -/// TODO(document) +/// C file Parser. #[derive(Default, Clone, Copy, Debug)] pub struct Parse; diff --git a/src/c/write_c.rs b/src/c/write_c.rs index e6e1e62..b7eeb1a 100644 --- a/src/c/write_c.rs +++ b/src/c/write_c.rs @@ -1,9 +1,8 @@ +use std::io::{Result, Write}; + use lang_c::ast::*; use lang_c::span::Node; -use core::ops::Deref; -use std::io::{Result, Write}; - use crate::write_base::*; impl WriteLine for Node { @@ -19,8 +18,8 @@ impl WriteString for Node { } impl WriteLine for TranslationUnit { - fn write_line(&self, _indent: usize, _write: &mut dyn Write) -> Result<()> { - todo!("Homework: write C") + fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> { + todo!() } } diff --git a/src/ir/dtype.rs b/src/ir/dtype.rs index 18d1f27..1d89086 100644 --- a/src/ir/dtype.rs +++ b/src/ir/dtype.rs @@ -1,13 +1,13 @@ use core::convert::TryFrom; use core::fmt; use core::ops::Deref; -use lang_c::ast; -use lang_c::span::Node; use std::collections::{HashMap, HashSet}; use std::hash::Hash; -use thiserror::Error; use itertools::izip; +use lang_c::ast; +use lang_c::span::Node; +use thiserror::Error; use crate::ir::*; use crate::some_or; diff --git a/src/ir/interp.rs b/src/ir/interp.rs index 3db7df4..c51412b 100644 --- a/src/ir/interp.rs +++ b/src/ir/interp.rs @@ -1,11 +1,9 @@ -use core::fmt; -use core::iter; -use core::mem; -use ordered_float::OrderedFloat; +use core::{fmt, iter, mem}; use std::collections::HashMap; -use thiserror::Error; use itertools::izip; +use ordered_float::OrderedFloat; +use thiserror::Error; use crate::ir::*; use crate::*; @@ -410,10 +408,12 @@ impl<'i> StackFrame<'i> { } mod calculator { + use std::cmp::Ordering; + + use lang_c::ast; + use super::Value; use crate::ir::*; - use lang_c::ast; - use std::cmp::Ordering; fn calculate_integer_binary_operator_expression( op: &ast::BinaryOperator, diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 2617bd1..e32c262 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -10,15 +10,15 @@ mod write_ir; use core::convert::TryFrom; use core::fmt; use core::ops::{Deref, DerefMut}; -use hexf_parse::{parse_hexf32, parse_hexf64}; -use itertools::Itertools; -use lang_c::ast; -use ordered_float::OrderedFloat; use std::collections::{BTreeMap, HashMap}; use std::hash::{Hash, Hasher}; pub use dtype::{Dtype, DtypeError, HasDtype}; +use hexf_parse::{parse_hexf32, parse_hexf64}; pub use interp::{interp, Value}; +use itertools::Itertools; +use lang_c::ast; +use ordered_float::OrderedFloat; pub use parse::Parse; pub use visualize::Visualizer; diff --git a/src/ir/parse.rs b/src/ir/parse.rs index 9fa6ed4..e4aafc2 100644 --- a/src/ir/parse.rs +++ b/src/ir/parse.rs @@ -5,8 +5,7 @@ use lang_c::*; use crate::ir::*; use crate::utils::AssertSupported; -use crate::Translate; -use crate::*; +use crate::{Translate, *}; peg::parser! { grammar ir_parse() for str { diff --git a/src/ir/visualize.rs b/src/ir/visualize.rs index 0c3ac85..6ea095f 100644 --- a/src/ir/visualize.rs +++ b/src/ir/visualize.rs @@ -3,8 +3,7 @@ use std::collections::HashMap; use crate::ir::*; -use crate::some_or; -use crate::Translate; +use crate::{some_or, Translate}; #[derive(Default, Debug)] pub struct Visualizer { diff --git a/src/ir/write_ir.rs b/src/ir/write_ir.rs index 766ddb4..77c1777 100644 --- a/src/ir/write_ir.rs +++ b/src/ir/write_ir.rs @@ -1,7 +1,6 @@ -use crate::ir::*; - use std::io::{Result, Write}; +use crate::ir::*; use crate::write_base::*; use crate::*; diff --git a/src/irgen/mod.rs b/src/irgen/mod.rs index 1a2e798..a7746c4 100644 --- a/src/irgen/mod.rs +++ b/src/irgen/mod.rs @@ -34,23 +34,22 @@ //! [irgen-stmt-1]: https://youtu.be/jFahkyxm994 //! [irgen-stmt-2]: https://youtu.be/UkaXaNw462U //! [github-qna-irgen]: https://github.com/kaist-cp/cs420/labels/homework%20-%20irgen -#![allow(dead_code)] +use core::cmp::Ordering; use core::convert::TryFrom; -use core::fmt; -use core::mem; +use core::{fmt, mem}; use std::collections::{BTreeMap, HashMap}; use std::ops::Deref; +use itertools::izip; use lang_c::ast::*; use lang_c::driver::Parse; use lang_c::span::Node; use thiserror::Error; use crate::ir::{DtypeError, HasDtype, Named}; +use crate::write_base::WriteString; use crate::*; -use itertools::izip; - #[derive(Debug)] pub struct IrgenError { pub code: String, @@ -69,6 +68,9 @@ impl fmt::Display for IrgenError { } } +/// Error format when a compiler error happens. +/// +/// Feel free to add more kinds of errors. #[derive(Debug, PartialEq, Eq, Error)] pub enum IrgenErrorMessage { /// For uncommon error @@ -89,11 +91,17 @@ pub enum IrgenErrorMessage { RequireLvalue { message: String }, } +/// A C file going through IR generation. #[derive(Default, Debug)] pub struct Irgen { + /// Declarations made in the C file (e.g, global variables and functions) decls: BTreeMap, + /// Type definitions made in the C file (e.g, typedef my_type = int;) typedefs: HashMap, + /// Structs defined in the C file, + // TODO: explain how to use this. structs: HashMap>, + /// Temporary counter for anonymous structs. One should not need to use this any more. struct_tempid_counter: usize, } @@ -112,14 +120,14 @@ impl Translate for Irgen { fn translate(&mut self, source: &TranslationUnit) -> Result { for ext_decl in &source.0 { - match ext_decl.node { - ExternalDeclaration::Declaration(ref var) => { + match &ext_decl.node { + ExternalDeclaration::Declaration(var) => { self.add_declaration(&var.node)?; } ExternalDeclaration::StaticAssert(_) => { panic!("ExternalDeclaration::StaticAssert is unsupported") } - ExternalDeclaration::FunctionDefinition(ref func) => { + ExternalDeclaration::FunctionDefinition(func) => { self.add_function_definition(&func.node)?; } } @@ -513,9 +521,10 @@ impl IrgenFunc<'_> { } /// Create a new allocation with type given by `alloc`. - fn insert_alloc(&mut self, alloc: Named) -> usize { + fn insert_alloc(&mut self, alloc: Named) -> ir::RegisterId { self.allocations.push(alloc); - self.allocations.len() - 1 + let id = self.allocations.len() - 1; + ir::RegisterId::local(id) } /// Insert a new block `context` with exit instruction `exit`. @@ -551,6 +560,7 @@ impl IrgenFunc<'_> { /// Panics if there are no scopes to exit, i.e, the function has a unmatched `}`. fn exit_scope(&mut self) { let _unused = self.symbol_table.pop().unwrap(); + debug_assert!(!self.symbol_table.is_empty()) } /// Inserts `var` with `value` to the current symbol table. @@ -576,15 +586,15 @@ impl IrgenFunc<'_> { /// `bid_continue` and break block `bid_break`. fn translate_stmt( &mut self, - _stmt: &Statement, - _context: &mut Context, - _bid_continue: Option, - _bid_break: Option, + stmt: &Statement, + context: &mut Context, + bid_continue: Option, + bid_break: Option, ) -> Result<(), IrgenError> { - todo!("Homework: IR Generation") + todo!() } - /// Translate parameter declaration of the functions to IR. + /// Translate initial parameter declarations of the functions to IR. /// /// For example, given the following C function from [`foo.c`][foo]: /// @@ -610,9 +620,11 @@ impl IrgenFunc<'_> { /// %b0:p0:i32:x /// %b0:p1:i32:y /// %b0:p2:i32:z + /// ... /// ``` /// /// With the following arguments : + /// /// ```ignore /// signature = FunctionSignature { ret: ir::INT, params: vec![ir::INT, ir::INT, ir::INT] } /// bid_init = 0 @@ -638,6 +650,7 @@ impl IrgenFunc<'_> { /// %b0:i0:unit = store %b0:p0:i32 %l0:i32* /// %b0:i1:unit = store %b0:p1:i32 %l1:i32* /// %b0:i2:unit = store %b0:p2:i32 %l2:i32* + /// ... /// ``` /// /// In particular, note that it is added to the local allocation list and store them to the @@ -649,12 +662,12 @@ impl IrgenFunc<'_> { /// [foo]: https://github.com/kaist-cp/kecc-public/blob/main/examples/c/foo.c fn translate_parameter_decl( &mut self, - _signature: &ir::FunctionSignature, - _bid_init: ir::BlockId, - _name_of_params: &[String], - _context: &mut Context, + signature: &ir::FunctionSignature, + bid_init: ir::BlockId, + name_of_params: &[String], + context: &mut Context, ) -> Result<(), IrgenErrorMessage> { - todo!("Homework: IR Generation") + todo!() } } diff --git a/src/lib.rs b/src/lib.rs index 74979dc..68fec8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,45 +5,50 @@ #![deny(warnings)] // Tries to deny all rustc allow lints. // -#![deny(absolute_paths_not_starting_with_crate)] -// Old, historical lint -// #![deny(box_pointers)] -#![deny(elided_lifetimes_in_paths)] -#![deny(explicit_outlives_requirements)] -#![deny(keyword_idents)] -#![deny(let_underscore_drop)] -#![deny(macro_use_extern_crate)] -#![deny(meta_variable_misuse)] -#![deny(missing_abi)] -#![deny(missing_copy_implementations)] -#![deny(missing_debug_implementations)] -// TODO -// #![deny(missing_docs)] -#![deny(non_ascii_idents)] -#![deny(noop_method_call)] -#![deny(rust_2021_incompatible_closure_captures)] -#![deny(rust_2021_incompatible_or_patterns)] -#![deny(rust_2021_prefixes_incompatible_syntax)] -#![deny(rust_2021_prelude_collisions)] -// Necessary for skeleton code. -// #![deny(single_use_lifetimes)] -#![deny(trivial_casts)] -#![deny(trivial_numeric_casts)] -// Necessary for skeleton code. -// #![deny(unreachable_pub)] -#![deny(unsafe_code)] -#![deny(unsafe_op_in_unsafe_fn)] -#![deny(unstable_features)] -// Necessary for `build-bin` trick. -// #![deny(unused_crate_dependencies)] -#![deny(unused_extern_crates)] -#![deny(unused_import_braces)] -#![deny(unused_lifetimes)] -#![deny(unused_macro_rules)] -#![deny(unused_qualifications)] -#![deny(unused_results)] -// Allowed for more flexible variants. -// #![deny(variant_size_differences)] +#![deny( + absolute_paths_not_starting_with_crate, + // Old, historical lint + // box_pointers, + elided_lifetimes_in_paths, + explicit_outlives_requirements, + keyword_idents, + let_underscore_drop, + macro_use_extern_crate, + meta_variable_misuse, + missing_abi, + // Most stuff are reasonably not copy. + // missing_copy_implementations, + missing_debug_implementations, + // TODO + // missing_docs + non_ascii_idents, + noop_method_call, + rust_2021_incompatible_closure_captures, + rust_2021_incompatible_or_patterns, + rust_2021_prefixes_incompatible_syntax, + rust_2021_prelude_collisions, + // Necessary for skeleton code. + // single_use_lifetimes, + trivial_casts, + trivial_numeric_casts, + // Necessary for skeleton code. + // unreachable_pub, + unsafe_code, + unsafe_op_in_unsafe_fn, + unstable_features, + // Necessary for `build-bin` trick. + // unused_crate_dependencies, + unused_extern_crates, + unused_import_braces, + unused_lifetimes, + unused_macro_rules, + unused_qualifications, + unused_results, + // Allowed for more flexible variants. + // variant_size_differences, +)] +// For skeleton code. +#![allow(unused)] mod tests; mod utils; @@ -57,17 +62,14 @@ mod asmgen; mod irgen; mod opt; -pub use tests::*; -pub use utils::*; -pub use write_base::write; - -pub use c::Parse; -pub use ir::Parse as IrParse; -pub use ir::Visualizer as IrVisualizer; - pub use asmgen::Asmgen; +pub use c::Parse; +pub use ir::{Parse as IrParse, Visualizer as IrVisualizer}; pub use irgen::Irgen; pub use opt::{ Deadcode, FunctionPass, Gvn, Mem2reg, Optimize, Repeat, SimplifyCfg, SimplifyCfgConstProp, SimplifyCfgEmpty, SimplifyCfgMerge, SimplifyCfgReach, O0, O1, }; +pub use tests::*; +pub use utils::*; +pub use write_base::write; diff --git a/src/opt/deadcode.rs b/src/opt/deadcode.rs index fbe4b3f..75b99f2 100644 --- a/src/opt/deadcode.rs +++ b/src/opt/deadcode.rs @@ -1,6 +1,9 @@ +use core::ops::Deref; +use std::collections::{HashMap, HashSet}; + use crate::ir::*; -use crate::opt::FunctionPass; -use crate::*; +use crate::opt::opt_utils::*; +use crate::opt::*; pub type Deadcode = FunctionPass>; @@ -8,7 +11,7 @@ pub type Deadcode = FunctionPass>; pub struct DeadcodeInner {} impl Optimize for DeadcodeInner { - fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("Homework: Deadcode Elimination") + fn optimize(&mut self, code: &mut FunctionDefinition) -> bool { + todo!() } } diff --git a/src/opt/gvn.rs b/src/opt/gvn.rs index 24bbf2e..bf95e4a 100644 --- a/src/opt/gvn.rs +++ b/src/opt/gvn.rs @@ -1,13 +1,20 @@ -use crate::opt::FunctionPass; -use crate::*; +use core::ops::Deref; +use std::collections::HashMap; + +use itertools::izip; +use lang_c::ast; + +use crate::ir::*; +use crate::opt::opt_utils::*; +use crate::opt::*; pub type Gvn = FunctionPass; #[derive(Default, Clone, Copy, Debug)] pub struct GvnInner {} -impl Optimize for GvnInner { - fn optimize(&mut self, _code: &mut ir::FunctionDefinition) -> bool { - todo!("Homework: Global Variable Numbering") +impl Optimize for GvnInner { + fn optimize(&mut self, code: &mut FunctionDefinition) -> bool { + todo!() } } diff --git a/src/opt/mem2reg.rs b/src/opt/mem2reg.rs index 6be8097..eda5bb8 100644 --- a/src/opt/mem2reg.rs +++ b/src/opt/mem2reg.rs @@ -1,6 +1,9 @@ +use core::ops::{Deref, DerefMut}; +use std::collections::{BTreeMap, HashMap, HashSet}; + use crate::ir::*; -use crate::opt::FunctionPass; -use crate::*; +use crate::opt::opt_utils::*; +use crate::opt::*; pub type Mem2reg = FunctionPass; @@ -8,7 +11,7 @@ pub type Mem2reg = FunctionPass; pub struct Mem2regInner {} impl Optimize for Mem2regInner { - fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("Homework: Register Promotion") + fn optimize(&mut self, code: &mut FunctionDefinition) -> bool { + todo!() } } diff --git a/src/opt/opt_utils.rs b/src/opt/opt_utils.rs index 30cc45d..fd0dc2d 100644 --- a/src/opt/opt_utils.rs +++ b/src/opt/opt_utils.rs @@ -1,5 +1,5 @@ //! Utilities for implementing optimizations. //! -//! You can add here utilities commonly used in the implementation of multiple optimizations. +//! You can freely add utilities commonly used in the implementation of multiple optimizations here. #![allow(dead_code)] diff --git a/src/opt/simplify_cfg.rs b/src/opt/simplify_cfg.rs index 9be7c10..d1c41f9 100644 --- a/src/opt/simplify_cfg.rs +++ b/src/opt/simplify_cfg.rs @@ -1,6 +1,11 @@ +use std::collections::{HashMap, HashSet}; +use std::ops::Deref; + +use itertools::izip; + use crate::ir::*; -use crate::opt::FunctionPass; -use crate::*; +use crate::opt::opt_utils::*; +use crate::opt::*; pub type SimplifyCfg = FunctionPass< Repeat<( @@ -26,25 +31,25 @@ pub struct SimplifyCfgMerge {} pub struct SimplifyCfgEmpty {} impl Optimize for SimplifyCfgConstProp { - fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("Homework: Simplify CFG") + fn optimize(&mut self, code: &mut FunctionDefinition) -> bool { + todo!() } } impl Optimize for SimplifyCfgReach { - fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("Homework: Simplify CFG") + fn optimize(&mut self, code: &mut FunctionDefinition) -> bool { + todo!() } } impl Optimize for SimplifyCfgMerge { - fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("Homework: Simplify CFG") + fn optimize(&mut self, code: &mut FunctionDefinition) -> bool { + todo!() } } impl Optimize for SimplifyCfgEmpty { - fn optimize(&mut self, _code: &mut FunctionDefinition) -> bool { - todo!("Homework: Simplify CFG") + fn optimize(&mut self, code: &mut FunctionDefinition) -> bool { + todo!() } } diff --git a/src/tests.rs b/src/tests.rs index c849ca5..d4b2b84 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,9 +1,10 @@ -use lang_c::*; -use rand::Rng; use std::fs::{self, File}; use std::io::{stderr, Read, Write}; use std::path::Path; use std::process::{Command, Stdio}; + +use lang_c::*; +use rand::Rng; use tempfile::tempdir; use wait_timeout::ChildExt; diff --git a/src/utils.rs b/src/utils.rs index ca9422e..4088ad2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,11 +1,7 @@ -use itertools::izip; - -use core::ops::Deref; - #[macro_export] /// Ok or executing the given expression. macro_rules! ok_or { - ($e:expr, $err:expr) => {{ + ($e:expr_2021, $err:expr_2021) => {{ match $e { Ok(r) => r, Err(_) => $err, @@ -16,7 +12,7 @@ macro_rules! ok_or { #[macro_export] /// Some or executing the given expression. macro_rules! some_or { - ($e:expr, $err:expr) => {{ + ($e:expr_2021, $err:expr_2021) => {{ match $e { Some(r) => r, None => $err, @@ -27,7 +23,7 @@ macro_rules! some_or { #[macro_export] /// Ok or exiting the process. macro_rules! ok_or_exit { - ($e:expr, $code:expr) => {{ + ($e:expr_2021, $code:expr_2021) => {{ match $e { Ok(r) => r, Err(e) => { @@ -41,7 +37,7 @@ macro_rules! ok_or_exit { #[macro_export] /// Ok or exiting the process. macro_rules! some_or_exit { - ($e:expr, $code:expr) => {{ + ($e:expr_2021, $code:expr_2021) => {{ match $e { Some(r) => r, None => ::std::process::exit($code), @@ -49,33 +45,41 @@ macro_rules! some_or_exit { }}; } -/// TODO(document) +/// Translates `S` to [`Translate::Target`]. +// TODO: Should this be in utils? pub trait Translate { - /// TODO(document) + /// The type to translate to. type Target; - /// TODO(document) + /// The error type. type Error; - /// TODO(document) + /// Translate `source` to `Self::Target`. fn translate(&mut self, source: &S) -> Result; } -/// TODO(document) +/// Trait to check if a type can be translated. pub trait AssertSupported { - /// TODO(document) + /// Assert that the type can be translated. + /// + /// # Panics + /// + /// Panics if the type can't be translated. + // TODO: should return a boolean. fn assert_supported(&self); } -/// TODO(document) +/// Essentially the same as [`PartialEq`]. +/// +/// Exists to check equaility on some foreign types. pub trait IsEquiv { - /// TODO(document) + /// See [`PartialEq::eq`]. fn is_equiv(&self, other: &Self) -> bool; } impl IsEquiv for Box { fn is_equiv(&self, other: &Self) -> bool { - self.deref().is_equiv(other.deref()) + (**self).is_equiv(&**other) } } @@ -97,6 +101,6 @@ impl IsEquiv for Option { impl IsEquiv for Vec { fn is_equiv(&self, other: &Self) -> bool { - self.len() == other.len() && izip!(self, other).all(|(lhs, rhs)| lhs.is_equiv(rhs)) + self.len() == other.len() && self.iter().zip(other).all(|(lhs, rhs)| lhs.is_equiv(rhs)) } } diff --git a/src/write_base.rs b/src/write_base.rs index 704b25a..3d87f57 100644 --- a/src/write_base.rs +++ b/src/write_base.rs @@ -1,7 +1,6 @@ use std::io::{Result, Write}; /// Write `indent` number of double spaces to `write`. -#[inline] pub fn write_indent(indent: usize, write: &mut dyn Write) -> Result<()> { write!(write, "{}", " ".repeat(indent)) } @@ -9,24 +8,21 @@ pub fn write_indent(indent: usize, write: &mut dyn Write) -> Result<()> { /// A trait for writing a type to a `Write` stream with a new line. pub trait WriteLine { /// Write `self` to `write`, starting at `indent` number of double spaces, with a newline at the - /// ned. + /// end. fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()>; } -/// Format types to a String. +/// Essentially the same as [`ToString`]. /// -/// Most cases, `fmt::Display` is used to format a type to a string. However, in some cases, we -/// can't implement `fmt::Display` for a type as it is defined in another crate. In such cases, we -/// can implement this trait to format the type to a string. +/// Exists to make some foreign types into a string. pub trait WriteString { - /// Change a type into a String. + /// See [`ToString::to_string`]. fn write_string(&self) -> String; } impl WriteString for Box { fn write_string(&self) -> String { - use core::ops::Deref; - self.deref().write_string() + (**self).write_string() } } @@ -36,6 +32,7 @@ impl WriteString for &T { } } +// Might be useful for debugging. impl WriteString for Option { fn write_string(&self) -> String { if let Some(this) = self {