From e41318d1ce8862f5dfd86bc2bc177541e2731978 Mon Sep 17 00:00:00 2001
From: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
Date: Tue, 18 Jul 2023 18:21:49 +0200
Subject: [PATCH] refactor(forge): refactor fmt command and parsing (#5428)
* refactor(forge): refactor fmt command and parsing
* chore: clippy
---
Cargo.lock | 1 -
cli/Cargo.toml | 1 -
cli/src/cmd/forge/fmt.rs | 303 ++++++++++++++++++---------------------
cli/src/utils.rs | 56 ++++----
fmt/src/comments.rs | 183 ++++++++++++-----------
fmt/src/helpers.rs | 4 +-
6 files changed, 271 insertions(+), 277 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 27f29332847b..18d642fae968 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2291,7 +2291,6 @@ dependencies = [
"clap_complete_fig",
"color-eyre",
"comfy-table",
- "console",
"criterion",
"dialoguer",
"dotenvy",
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 7376d9da7525..acf373da341b 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -34,7 +34,6 @@ yansi = "0.5"
tracing-error = "0.2"
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter", "fmt"] }
tracing = "0.1"
-console = "0.15"
watchexec = "2"
is-terminal = "0.4"
comfy-table = "6"
diff --git a/cli/src/cmd/forge/fmt.rs b/cli/src/cmd/forge/fmt.rs
index ee4d1130133c..e199947e3f1c 100644
--- a/cli/src/cmd/forge/fmt.rs
+++ b/cli/src/cmd/forge/fmt.rs
@@ -3,20 +3,20 @@ use crate::{
utils::FoundryPathExt,
};
use clap::{Parser, ValueHint};
-use console::{style, Style};
use forge_fmt::{format, parse, print_diagnostics_report};
use foundry_common::{fs, term::cli_warn};
-use foundry_config::{impl_figment_convert_basic, Config};
+use foundry_config::impl_figment_convert_basic;
use foundry_utils::glob::expand_globs;
use rayon::prelude::*;
use similar::{ChangeTag, TextDiff};
use std::{
fmt::{self, Write},
io,
- io::Read,
+ io::{Read, Write as _},
path::{Path, PathBuf},
};
use tracing::log::warn;
+use yansi::Color;
/// CLI arguments for `forge fmt`.
#[derive(Debug, Clone, Parser)]
@@ -48,38 +48,6 @@ impl_figment_convert_basic!(FmtArgs);
// === impl FmtArgs ===
-impl FmtArgs {
- /// Returns all inputs to format
- fn inputs(&self, config: &Config) -> Vec {
- if self.paths.is_empty() {
- return config.project_paths().input_files().into_iter().map(Input::Path).collect()
- }
-
- let mut paths = self.paths.iter().peekable();
-
- if let Some(path) = paths.peek() {
- let mut stdin = io::stdin();
- if *path == Path::new("-") && !is_terminal::is_terminal(&stdin) {
- let mut buf = String::new();
- stdin.read_to_string(&mut buf).expect("Failed to read from stdin");
- return vec![Input::Stdin(buf)]
- }
- }
-
- let mut out = Vec::with_capacity(self.paths.len());
- for path in self.paths.iter() {
- if path.is_dir() {
- out.extend(ethers::solc::utils::source_files(path).into_iter().map(Input::Path));
- } else if path.is_sol() {
- out.push(Input::Path(path.to_path_buf()));
- } else {
- warn!("Cannot process path {}", path.display());
- }
- }
- out
- }
-}
-
impl Cmd for FmtArgs {
type Output = ();
@@ -90,137 +58,115 @@ impl Cmd for FmtArgs {
let ignored = expand_globs(&config.__root.0, config.fmt.ignore.iter())?;
let cwd = std::env::current_dir()?;
- let mut inputs = vec![];
- for input in self.inputs(&config) {
- match input {
- Input::Path(p) => {
- if (p.is_absolute() && !ignored.contains(&p)) ||
- !ignored.contains(&cwd.join(&p))
+ let input = match &self.paths[..] {
+ [] => Input::Paths(config.project_paths().input_files_iter().collect()),
+ [one] if one == Path::new("-") => {
+ let mut s = String::new();
+ io::stdin().read_to_string(&mut s).expect("Failed to read from stdin");
+ Input::Stdin(s)
+ }
+ paths => {
+ let mut inputs = Vec::with_capacity(paths.len());
+ for path in paths {
+ if !ignored.is_empty() &&
+ ((path.is_absolute() && ignored.contains(path)) ||
+ ignored.contains(&cwd.join(path)))
{
- inputs.push(Input::Path(p));
+ continue
}
+
+ if path.is_dir() {
+ inputs.extend(ethers::solc::utils::source_files_iter(path));
+ } else if path.is_sol() {
+ inputs.push(path.to_path_buf());
+ } else {
+ warn!("Cannot process path {}", path.display());
+ }
+ }
+ Input::Paths(inputs)
+ }
+ };
+
+ let format = |source: String, path: Option<&Path>| -> eyre::Result<_> {
+ let name = match path {
+ Some(path) => {
+ path.strip_prefix(&config.__root.0).unwrap_or(path).display().to_string()
}
- other => inputs.push(other),
+ None => "stdin".to_string(),
};
- }
- if inputs.is_empty() {
- cli_warn!("Nothing to format.\nHINT: If you are working outside of the project, try providing paths to your source files: `forge fmt `");
- return Ok(())
- }
+ let parsed = parse(&source).map_err(|diagnostics| {
+ let _ = print_diagnostics_report(&source, path, diagnostics);
+ eyre::eyre!("Failed to parse Solidity code for {name}. Leaving source unchanged.")
+ })?;
+
+ if !parsed.invalid_inline_config_items.is_empty() {
+ for (loc, warning) in &parsed.invalid_inline_config_items {
+ let mut lines = source[..loc.start().min(source.len())].split('\n');
+ let col = lines.next_back().unwrap().len() + 1;
+ let row = lines.count() + 1;
+ cli_warn!("[{}:{}:{}] {}", name, row, col, warning);
+ }
+ }
- let diffs = inputs
- .par_iter()
- .map(|input| {
- let source = match input {
- Input::Path(path) => fs::read_to_string(path)?,
- Input::Stdin(source) => source.to_string()
- };
+ let mut output = String::new();
+ format(&mut output, parsed, config.fmt.clone()).unwrap();
- let parsed = match parse(&source) {
- Ok(result) => result,
- Err(diagnostics) => {
- let path = if let Input::Path(path) = input {Some(path)} else {None};
- print_diagnostics_report(&source,path, diagnostics)?;
- eyre::bail!(
- "Failed to parse Solidity code for {input}. Leaving source unchanged."
- )
- }
- };
+ solang_parser::parse(&output, 0).map_err(|diags| {
+ eyre::eyre!(
+ "Failed to construct valid Solidity code for {name}. Leaving source unchanged.\n\
+ Debug info: {diags:?}"
+ )
+ })?;
- if !parsed.invalid_inline_config_items.is_empty() {
- let path = match input {
- Input::Path(path) => {
- let path = path.strip_prefix(&config.__root.0).unwrap_or(path);
- format!("{}", path.display())
- }
- Input::Stdin(_) => "stdin".to_string()
- };
- for (loc, warning) in &parsed.invalid_inline_config_items {
- let mut lines = source[..loc.start().min(source.len())].split('\n');
- let col = lines.next_back().unwrap().len() + 1;
- let row = lines.count() + 1;
- cli_warn!("[{}:{}:{}] {}", path, row, col, warning);
- }
+ if self.check || path.is_none() {
+ if self.raw {
+ print!("{output}");
}
- let mut output = String::new();
- format(&mut output, parsed, config.fmt.clone()).unwrap();
-
- solang_parser::parse(&output, 0).map_err(|diags| {
- eyre::eyre!(
- "Failed to construct valid Solidity code for {}. Leaving source unchanged.\n\
- Debug info: {:?}",
- input,
- diags,
- )
- })?;
-
- if self.check || matches!(input, Input::Stdin(_)) {
- if self.raw {
- print!("{output}");
- }
-
- let diff = TextDiff::from_lines(&source, &output);
-
- if diff.ratio() < 1.0 {
- let mut diff_summary = String::new();
-
- writeln!(diff_summary, "Diff in {input}:")?;
- for (j, group) in diff.grouped_ops(3).iter().enumerate() {
- if j > 0 {
- writeln!(diff_summary, "{:-^1$}", "-", 80)?;
- }
- for op in group {
- for change in diff.iter_inline_changes(op) {
- let (sign, s) = match change.tag() {
- ChangeTag::Delete => ("-", Style::new().red()),
- ChangeTag::Insert => ("+", Style::new().green()),
- ChangeTag::Equal => (" ", Style::new().dim()),
- };
- write!(
- diff_summary,
- "{}{} |{}",
- style(Line(change.old_index())).dim(),
- style(Line(change.new_index())).dim(),
- s.apply_to(sign).bold(),
- )?;
- for (emphasized, value) in change.iter_strings_lossy() {
- if emphasized {
- write!(diff_summary, "{}", s.apply_to(value).underlined().on_black())?;
- } else {
- write!(diff_summary, "{}", s.apply_to(value))?;
- }
- }
- if change.missing_newline() {
- writeln!(diff_summary)?;
- }
- }
- }
- }
-
- return Ok(Some(diff_summary))
- }
- } else if let Input::Path(path) = input {
- fs::write(path, output)?;
+ let diff = TextDiff::from_lines(&source, &output);
+ if diff.ratio() < 1.0 {
+ return Ok(Some(format_diff_summary(&name, &diff)))
}
+ } else if let Some(path) = path {
+ fs::write(path, output)?;
+ }
+ Ok(None)
+ };
+
+ let diffs = match input {
+ Input::Stdin(source) => format(source, None).map(|diff| vec![diff]),
+ Input::Paths(paths) => {
+ if paths.is_empty() {
+ cli_warn!(
+ "Nothing to format.\n\
+ HINT: If you are working outside of the project, \
+ try providing paths to your source files: `forge fmt `"
+ );
+ return Ok(())
+ }
+ paths
+ .par_iter()
+ .map(|path| {
+ let source = fs::read_to_string(path)?;
+ format(source, Some(path))
+ })
+ .collect()
+ }
+ }?;
- Ok(None)
- })
- .collect::>>()?
- .into_iter()
- .flatten()
- .collect::>();
-
- if !diffs.is_empty() {
+ let mut diffs = diffs.iter().flatten();
+ if let Some(first) = diffs.next() {
// This branch is only reachable with stdin or --check
if !self.raw {
- for (i, diff) in diffs.iter().enumerate() {
+ let mut stdout = io::stdout().lock();
+ let first = std::iter::once(first);
+ for (i, diff) in first.chain(diffs).enumerate() {
if i > 0 {
- println!();
+ let _ = stdout.write_all(b"\n");
}
- print!("{diff}");
+ let _ = stdout.write_all(diff.as_bytes());
}
}
@@ -237,24 +183,61 @@ struct Line(Option);
#[derive(Debug)]
enum Input {
- Path(PathBuf),
Stdin(String),
-}
-
-impl fmt::Display for Input {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Input::Path(path) => write!(f, "{}", path.display()),
- Input::Stdin(_) => write!(f, "stdin"),
- }
- }
+ Paths(Vec),
}
impl fmt::Display for Line {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
- None => write!(f, " "),
+ None => f.write_str(" "),
Some(idx) => write!(f, "{:<4}", idx + 1),
}
}
}
+
+fn format_diff_summary<'a, 'b, 'r>(name: &str, diff: &'r TextDiff<'a, 'b, '_, str>) -> String
+where
+ 'r: 'a + 'b,
+{
+ let cap = 128;
+ let mut diff_summary = String::with_capacity(cap);
+
+ let _ = writeln!(diff_summary, "Diff in {name}:");
+ for (j, group) in diff.grouped_ops(3).into_iter().enumerate() {
+ if j > 0 {
+ let s =
+ "--------------------------------------------------------------------------------";
+ diff_summary.push_str(s);
+ }
+ for op in group {
+ for change in diff.iter_inline_changes(&op) {
+ let dimmed = Color::Default.style().dimmed();
+ let (sign, s) = match change.tag() {
+ ChangeTag::Delete => ("-", Color::Red.style()),
+ ChangeTag::Insert => ("+", Color::Green.style()),
+ ChangeTag::Equal => (" ", dimmed),
+ };
+
+ let _ = write!(
+ diff_summary,
+ "{}{} |{}",
+ dimmed.paint(Line(change.old_index())),
+ dimmed.paint(Line(change.new_index())),
+ s.bold().paint(sign),
+ );
+
+ for (emphasized, value) in change.iter_strings_lossy() {
+ let s = if emphasized { s.underline().bg(Color::Black) } else { s };
+ let _ = write!(diff_summary, "{}", s.paint(value));
+ }
+
+ if change.missing_newline() {
+ diff_summary.push('\n');
+ }
+ }
+ }
+ }
+
+ diff_summary
+}
diff --git a/cli/src/utils.rs b/cli/src/utils.rs
index 23d1e2c20d66..1caaa694553b 100644
--- a/cli/src/utils.rs
+++ b/cli/src/utils.rs
@@ -1,4 +1,3 @@
-use console::Emoji;
use ethers::{
abi::token::{LenientTokenizer, Tokenizer},
prelude::TransactionReceipt,
@@ -203,40 +202,33 @@ pub fn enable_paint() {
/// Prints parts of the receipt to stdout
pub fn print_receipt(chain: Chain, receipt: &TransactionReceipt) {
- let contract_address = receipt
- .contract_address
- .map(|addr| format!("\nContract Address: {}", to_checksum(&addr, None)))
- .unwrap_or_default();
-
let gas_used = receipt.gas_used.unwrap_or_default();
let gas_price = receipt.effective_gas_price.unwrap_or_default();
-
- let gas_details = if gas_price.is_zero() {
- format!("Gas Used: {gas_used}")
- } else {
- let paid = format_units(gas_used.mul(gas_price), 18).unwrap_or_else(|_| "N/A".into());
- let gas_price = format_units(gas_price, 9).unwrap_or_else(|_| "N/A".into());
- format!(
- "Paid: {} ETH ({gas_used} gas * {} gwei)",
- paid.trim_end_matches('0'),
- gas_price.trim_end_matches('0').trim_end_matches('.')
- )
- };
-
- let check = if receipt.status.unwrap_or_default().is_zero() {
- Emoji("❌ ", " [Failed] ")
- } else {
- Emoji("✅ ", " [Success] ")
- };
-
println!(
- "\n##### {}\n{}Hash: 0x{}{}\nBlock: {}\n{}\n",
- chain,
- check,
- hex::encode(receipt.transaction_hash.as_bytes()),
- contract_address,
- receipt.block_number.unwrap_or_default(),
- gas_details
+ "\n##### {chain}\n{status}Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n",
+ status = if receipt.status.map_or(true, |s| s.is_zero()) {
+ "❌ [Failed]"
+ } else {
+ "✅ [Success]"
+ },
+ tx_hash = receipt.transaction_hash,
+ caddr = if let Some(addr) = &receipt.contract_address {
+ format!("\nContract Address: {}", to_checksum(addr, None))
+ } else {
+ String::new()
+ },
+ bn = receipt.block_number.unwrap_or_default(),
+ gas = if gas_price.is_zero() {
+ format!("Gas Used: {gas_used}")
+ } else {
+ let paid = format_units(gas_used.mul(gas_price), 18).unwrap_or_else(|_| "N/A".into());
+ let gas_price = format_units(gas_price, 9).unwrap_or_else(|_| "N/A".into());
+ format!(
+ "Paid: {} ETH ({gas_used} gas * {} gwei)",
+ paid.trim_end_matches('0'),
+ gas_price.trim_end_matches('0').trim_end_matches('.')
+ )
+ },
);
}
diff --git a/fmt/src/comments.rs b/fmt/src/comments.rs
index 5ee62d38bb2a..a76c64ffc53a 100644
--- a/fmt/src/comments.rs
+++ b/fmt/src/comments.rs
@@ -86,74 +86,63 @@ impl CommentWithMetadata {
let this_line =
if src_before.ends_with('\n') { "" } else { lines_before.next().unwrap_or_default() };
let indent_len = this_line.chars().take_while(|c| c.is_whitespace()).count();
- let last_line = lines_before.next();
+ let last_line = lines_before.next().map(str::trim_start);
if matches!(comment, Comment::DocLine(..) | Comment::DocBlock(..)) {
return Self::new(
comment,
CommentPosition::Prefix,
- last_line.unwrap_or_default().trim_start().is_empty(),
+ last_line.map_or(true, str::is_empty),
indent_len,
)
}
- let code_end = src_before
- .comment_state_char_indices()
- .filter_map(|(state, idx, ch)| {
- if matches!(state, CommentState::None) && !ch.is_whitespace() {
- Some(idx)
+ // TODO: this loop takes almost the entirety of the time spent in parsing, which is up to
+ // 80% of `crate::fmt`
+ let mut code_end = 0;
+ for (state, idx, ch) in src_before.comment_state_char_indices() {
+ if matches!(state, CommentState::None) && !ch.is_whitespace() {
+ code_end = idx;
+ }
+ }
+
+ let (position, has_newline_before) = if src_before[code_end..].contains('\n') {
+ // comment sits on a line without code
+ if let Some(last_line) = last_line {
+ if last_line.is_empty() {
+ // line before is empty
+ (CommentPosition::Prefix, true)
} else {
- None
- }
- })
- .last()
- .unwrap_or_default();
-
- let (position, has_newline_before) = {
- if src_before[code_end..].contains('\n') {
- // comment sits on a line without code
- if let Some(last_line) = last_line {
- if last_line.trim_start().is_empty() {
- // line before is empty
- (CommentPosition::Prefix, true)
- } else {
- // line has something
- // check if the last comment after code was a postfix comment
- if last_comment
- .filter(|last_comment| {
- last_comment.loc.end() > code_end && !last_comment.is_prefix()
- })
- .is_some()
- {
- // get the indent size of the next item of code
- let next_indent_len = src[comment.loc().end()..]
- .non_comment_chars()
- .take_while(|ch| ch.is_whitespace())
- .fold(
- indent_len,
- |indent, ch| if ch == '\n' { 0 } else { indent + 1 },
- );
- if indent_len > next_indent_len {
- // the comment indent is bigger than the next code indent
- (CommentPosition::Postfix, false)
- } else {
- // the comment indent is equal to or less than the next code
- // indent
- (CommentPosition::Prefix, false)
- }
+ // line has something
+ // check if the last comment after code was a postfix comment
+ if last_comment
+ .map_or(false, |last| last.loc.end() > code_end && !last.is_prefix())
+ {
+ // get the indent size of the next item of code
+ let next_indent_len = src[comment.loc().end()..]
+ .non_comment_chars()
+ .take_while(|ch| ch.is_whitespace())
+ .fold(indent_len, |indent, ch| if ch == '\n' { 0 } else { indent + 1 });
+ if indent_len > next_indent_len {
+ // the comment indent is bigger than the next code indent
+ (CommentPosition::Postfix, false)
} else {
- // if there is no postfix comment after the piece of code
+ // the comment indent is equal to or less than the next code
+ // indent
(CommentPosition::Prefix, false)
}
+ } else {
+ // if there is no postfix comment after the piece of code
+ (CommentPosition::Prefix, false)
}
- } else {
- // beginning of file
- (CommentPosition::Prefix, false)
}
} else {
- // comment is after some code
- (CommentPosition::Postfix, false)
+ // beginning of file
+ (CommentPosition::Prefix, false)
}
+ } else {
+ // comment is after some code
+ (CommentPosition::Postfix, false)
};
Self::new(comment, position, has_newline_before, indent_len)
@@ -171,15 +160,23 @@ impl CommentWithMetadata {
self.loc.start() < byte
}
+ /// Returns the contents of the comment without the start and end tokens
pub fn contents(&self) -> &str {
- self.comment
- .strip_prefix(self.start_token())
- .map(|c| self.end_token().and_then(|end| c.strip_suffix(end)).unwrap_or(c))
- .unwrap_or(&self.comment)
+ let mut s = self.comment.as_str();
+ if let Some(stripped) = s.strip_prefix(self.start_token()) {
+ s = stripped;
+ }
+ if let Some(end_token) = self.end_token() {
+ if let Some(stripped) = s.strip_suffix(end_token) {
+ s = stripped;
+ }
+ }
+ s
}
/// The start token of the comment
- pub fn start_token(&self) -> &str {
+ #[inline]
+ pub const fn start_token(&self) -> &'static str {
match self.ty {
CommentType::Line => "//",
CommentType::Block => "/*",
@@ -190,7 +187,8 @@ impl CommentWithMetadata {
/// The token that gets written on the newline when the
/// comment is wrapped
- pub fn wrap_token(&self) -> &str {
+ #[inline]
+ pub const fn wrap_token(&self) -> &'static str {
match self.ty {
CommentType::Line => "// ",
CommentType::DocLine => "/// ",
@@ -200,7 +198,8 @@ impl CommentWithMetadata {
}
/// The end token of the comment
- pub fn end_token(&self) -> Option<&str> {
+ #[inline]
+ pub const fn end_token(&self) -> Option<&'static str> {
match self.ty {
CommentType::Line | CommentType::DocLine => None,
CommentType::Block | CommentType::DocBlock => Some("*/"),
@@ -217,20 +216,16 @@ pub struct Comments {
impl Comments {
pub fn new(mut comments: Vec, src: &str) -> Self {
- let mut prefixes = VecDeque::new();
- let mut postfixes = VecDeque::new();
+ let mut prefixes = VecDeque::with_capacity(comments.len());
+ let mut postfixes = VecDeque::with_capacity(comments.len());
let mut last_comment = None;
comments.sort_by_key(|comment| comment.loc());
for comment in comments {
- let comment =
- CommentWithMetadata::from_comment_and_src(comment, src, last_comment.as_ref());
- last_comment = Some(comment.clone());
- if comment.is_prefix() {
- prefixes.push_back(comment)
- } else {
- postfixes.push_back(comment)
- }
+ let comment = CommentWithMetadata::from_comment_and_src(comment, src, last_comment);
+ let vec = if comment.is_prefix() { &mut prefixes } else { &mut postfixes };
+ vec.push_back(comment);
+ last_comment = Some(vec.back().unwrap());
}
Self { prefixes, postfixes }
}
@@ -326,36 +321,42 @@ pub enum CommentState {
/// An Iterator over characters and indices in a string slice with information about the state of
/// comments
pub struct CommentStateCharIndices<'a> {
- iter: std::iter::Peekable>,
+ iter: std::str::CharIndices<'a>,
state: CommentState,
}
impl<'a> CommentStateCharIndices<'a> {
+ #[inline]
fn new(string: &'a str) -> Self {
- Self { iter: string.char_indices().peekable(), state: CommentState::None }
+ Self { iter: string.char_indices(), state: CommentState::None }
}
+
+ #[inline]
pub fn with_state(mut self, state: CommentState) -> Self {
self.state = state;
self
}
+
+ #[inline]
+ pub fn peek(&mut self) -> Option<(usize, char)> {
+ self.iter.clone().next()
+ }
}
-impl<'a> Iterator for CommentStateCharIndices<'a> {
+impl Iterator for CommentStateCharIndices<'_> {
type Item = (CommentState, usize, char);
+
+ #[inline]
fn next(&mut self) -> Option {
let (idx, ch) = self.iter.next()?;
match self.state {
CommentState::None => {
if ch == '/' {
- match self.iter.peek() {
- Some((_, '/')) => {
- self.state = CommentState::LineStart1;
- }
- Some((_, '*')) => {
- self.state = CommentState::BlockStart1;
- }
- _ => {}
- }
+ self.state = match self.peek() {
+ Some((_, '/')) => CommentState::LineStart1,
+ Some((_, '*')) => CommentState::BlockStart1,
+ _ => CommentState::None,
+ };
}
}
CommentState::LineStart1 => {
@@ -377,7 +378,7 @@ impl<'a> Iterator for CommentStateCharIndices<'a> {
}
CommentState::Block => {
if ch == '*' {
- if let Some((_, '/')) = self.iter.peek() {
+ if let Some((_, '/')) = self.peek() {
self.state = CommentState::BlockEnd1;
}
}
@@ -391,13 +392,27 @@ impl<'a> Iterator for CommentStateCharIndices<'a> {
}
Some((self.state, idx, ch))
}
+
+ #[inline]
+ fn count(self) -> usize {
+ self.iter.count()
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option) {
+ self.iter.size_hint()
+ }
}
+impl std::iter::FusedIterator for CommentStateCharIndices<'_> {}
+
/// An Iterator over characters in a string slice which are not a apart of comments
pub struct NonCommentChars<'a>(CommentStateCharIndices<'a>);
impl<'a> Iterator for NonCommentChars<'a> {
type Item = char;
+
+ #[inline]
fn next(&mut self) -> Option {
for (state, _, ch) in self.0.by_ref() {
if state == CommentState::None {
@@ -411,9 +426,13 @@ impl<'a> Iterator for NonCommentChars<'a> {
/// Helpers for iterating over comment containing strings
pub trait CommentStringExt {
fn comment_state_char_indices(&self) -> CommentStateCharIndices;
+
+ #[inline]
fn non_comment_chars(&self) -> NonCommentChars {
NonCommentChars(self.comment_state_char_indices())
}
+
+ #[inline]
fn trim_comments(&self) -> String {
self.non_comment_chars().collect()
}
@@ -423,12 +442,14 @@ impl CommentStringExt for T
where
T: AsRef,
{
+ #[inline]
fn comment_state_char_indices(&self) -> CommentStateCharIndices {
CommentStateCharIndices::new(self.as_ref())
}
}
impl CommentStringExt for str {
+ #[inline]
fn comment_state_char_indices(&self) -> CommentStateCharIndices {
CommentStateCharIndices::new(self)
}
diff --git a/fmt/src/helpers.rs b/fmt/src/helpers.rs
index 49b04c0a484e..d419b88a9d4a 100644
--- a/fmt/src/helpers.rs
+++ b/fmt/src/helpers.rs
@@ -5,7 +5,7 @@ use crate::{
use ariadne::{Color, Fmt, Label, Report, ReportKind, Source};
use itertools::Itertools;
use solang_parser::{diagnostics::Diagnostic, pt::*};
-use std::{fmt::Write, path::PathBuf};
+use std::{fmt::Write, path::Path};
/// Result of parsing the source code
#[derive(Debug)]
@@ -75,7 +75,7 @@ pub fn offset_to_line_column(content: &str, start: usize) -> (usize, usize) {
/// Print the report of parser's diagnostics
pub fn print_diagnostics_report(
content: &str,
- path: Option<&PathBuf>,
+ path: Option<&Path>,
diagnostics: Vec,
) -> std::io::Result<()> {
let filename =