From 49f99f61524072c48af7e835859f0b298bef449d Mon Sep 17 00:00:00 2001 From: Omar Abdulla Date: Fri, 15 Aug 2025 06:50:06 +0300 Subject: [PATCH] Implement compilation modes --- crates/common/src/cached_fs/mod.rs | 49 ++ .../src/types/version_or_requirement.rs | 28 +- crates/core/src/driver/mod.rs | 9 +- crates/core/src/main.rs | 33 +- crates/format/src/case.rs | 13 +- crates/format/src/metadata.rs | 46 +- crates/format/src/mode.rs | 454 ++++++++++++++---- crates/report/src/reporter.rs | 10 +- 8 files changed, 529 insertions(+), 113 deletions(-) create mode 100644 crates/common/src/cached_fs/mod.rs diff --git a/crates/common/src/cached_fs/mod.rs b/crates/common/src/cached_fs/mod.rs new file mode 100644 index 0000000..17a1fa5 --- /dev/null +++ b/crates/common/src/cached_fs/mod.rs @@ -0,0 +1,49 @@ +//! This module implements a cached file system allowing for results to be stored in-memory rather +//! rather being queried from the file system again. + +use std::fs; +use std::io::{Error, Result}; +use std::path::{Path, PathBuf}; + +use moka::sync::Cache; +use once_cell::sync::Lazy; + +pub fn read(path: impl AsRef) -> Result> { + static READ_CACHE: Lazy>> = Lazy::new(|| Cache::new(10_000)); + + let path = path.as_ref().canonicalize()?; + match READ_CACHE.get(path.as_path()) { + Some(content) => Ok(content), + None => { + let content = fs::read(path.as_path())?; + READ_CACHE.insert(path, content.clone()); + Ok(content) + } + } +} + +pub fn read_to_string(path: impl AsRef) -> Result { + let content = read(path)?; + String::from_utf8(content).map_err(|_| { + Error::new( + std::io::ErrorKind::InvalidData, + "The contents of the file are not valid UTF8", + ) + }) +} + +pub fn read_dir(path: impl AsRef) -> Result>>> { + static READ_DIR_CACHE: Lazy>> = Lazy::new(|| Cache::new(10_000)); + + let path = path.as_ref().canonicalize()?; + match READ_DIR_CACHE.get(path.as_path()) { + Some(entries) => Ok(Box::new(entries.into_iter().map(Ok)) as Box<_>), + None => { + let entries = fs::read_dir(path.as_path())? + .flat_map(|maybe_entry| maybe_entry.map(|entry| entry.path())) + .collect(); + READ_DIR_CACHE.insert(path.clone(), entries); + Ok(read_dir(path).unwrap()) + } + } +} diff --git a/crates/common/src/types/version_or_requirement.rs b/crates/common/src/types/version_or_requirement.rs index 787a37d..a511958 100644 --- a/crates/common/src/types/version_or_requirement.rs +++ b/crates/common/src/types/version_or_requirement.rs @@ -1,6 +1,9 @@ +use std::{fmt::Display, str::FromStr}; + +use anyhow::{Error, bail}; use semver::{Version, VersionReq}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum VersionOrRequirement { Version(Version), Requirement(VersionReq), @@ -39,3 +42,26 @@ impl TryFrom for VersionReq { Ok(requirement) } } + +impl FromStr for VersionOrRequirement { + type Err = Error; + + fn from_str(s: &str) -> Result { + if let Ok(version) = Version::parse(s) { + Ok(Self::Version(version)) + } else if let Ok(version_req) = VersionReq::parse(s) { + Ok(Self::Requirement(version_req)) + } else { + bail!("Not a valid version or version requirement") + } + } +} + +impl Display for VersionOrRequirement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + VersionOrRequirement::Version(version) => version.fmt(f), + VersionOrRequirement::Requirement(version_req) => version_req.fmt(f), + } + } +} diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index a8e908e..ab61ed2 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -35,7 +35,7 @@ use revive_dt_format::metadata::{ContractInstance, ContractPathAndIdent}; use revive_dt_format::{input::Step, metadata::Metadata}; use revive_dt_node::Node; use revive_dt_node_interaction::EthereumNode; -use tracing::Instrument; +use tracing::{Instrument, instrument}; use crate::Platform; @@ -152,6 +152,7 @@ where } /// Handles the contract deployment for a given input performing it if it needs to be performed. + #[instrument(level = "info", skip_all, ret)] async fn handle_input_contract_deployment( &mut self, metadata: &Metadata, @@ -208,6 +209,7 @@ where } /// Handles the execution of the input in terms of the calls that need to be made. + #[instrument(level = "info", skip_all, ret)] async fn handle_input_execution( &mut self, input: &Input, @@ -252,6 +254,7 @@ where } } + #[instrument(level = "info", skip_all, ret)] async fn handle_input_call_frame_tracing( &self, execution_receipt: &TransactionReceipt, @@ -279,6 +282,7 @@ where }) } + #[instrument(level = "info", skip_all, ret)] fn handle_input_variable_assignment( &mut self, input: &Input, @@ -309,6 +313,7 @@ where Ok(()) } + #[instrument(level = "info", skip_all, ret)] async fn handle_input_expectations( &mut self, input: &Input, @@ -360,6 +365,7 @@ where Ok(()) } + #[instrument(level = "info", skip_all, ret)] async fn handle_input_expectation_item( &mut self, execution_receipt: &TransactionReceipt, @@ -502,6 +508,7 @@ where Ok(()) } + #[instrument(level = "info", skip_all, ret)] async fn handle_input_diff( &mut self, _: CaseIdx, diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index 55ec7d3..68ede7a 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -34,7 +34,7 @@ use revive_dt_format::{ corpus::Corpus, input::{Input, Step}, metadata::{ContractInstance, ContractPathAndIdent, Metadata, MetadataFile}, - mode::SolcMode, + mode::{Mode, SolcMode}, }; use revive_dt_node::pool::NodePool; use revive_dt_report::reporter::{Report, Span}; @@ -172,8 +172,7 @@ where .iter() .enumerate() .flat_map(move |(case_idx, case)| { - metadata - .solc_modes() + SolcMode::ALL .into_iter() .map(move |solc_mode| (path, metadata, case_idx, case, solc_mode)) }) @@ -226,6 +225,27 @@ where } None => true, }) + .flat_map(|(path, metadata, case_idx, case, solc_mode)| { + if let Some(ref case_modes) = case.modes { + case_modes + .iter() + .filter_map(Mode::as_solc_mode) + .filter(|case_mode| case_mode.matches(&solc_mode)) + .map(|case_mode| (path, metadata, case_idx, case, case_mode.clone())) + .collect::>() + .into_iter() + } else if let Some(ref metadata_modes) = metadata.modes { + metadata_modes + .iter() + .filter_map(Mode::as_solc_mode) + .filter(|metadata_mode| metadata_mode.matches(&solc_mode)) + .map(|metadata_mode| (path, metadata, case_idx, case, metadata_mode.clone())) + .collect::>() + .into_iter() + } else { + vec![(path, metadata, case_idx, case, solc_mode)].into_iter() + } + }) .map(|(metadata_file_path, metadata, case_idx, case, solc_mode)| { Test { metadata: metadata.clone(), @@ -328,13 +348,13 @@ async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test, CaseR Ok(_inputs) => { number_of_successes += 1; eprintln!( - "{GREEN}Case Succeeded:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode:?})" + "{GREEN}Case Succeeded:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode})" ); } Err(err) => { number_of_failures += 1; eprintln!( - "{RED}Case Failed:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode:?})" + "{RED}Case Failed:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode})" ); failures.push((test, err)); } @@ -695,7 +715,8 @@ async fn compile_contracts( let compiler = Compiler::::new() .with_allow_path(metadata.directory()?) - .with_optimization(mode.solc_optimize()); + .with_optimization(mode.optimize) + .with_via_ir(mode.via_ir); let mut compiler = metadata .files_to_compile()? .try_fold(compiler, |compiler, path| compiler.with_source(&path))?; diff --git a/crates/format/src/case.rs b/crates/format/src/case.rs index aafe914..3b52010 100644 --- a/crates/format/src/case.rs +++ b/crates/format/src/case.rs @@ -1,9 +1,12 @@ +use std::collections::HashSet; + use serde::{Deserialize, Serialize}; use revive_dt_common::macros::define_wrapper_type; use crate::{ input::{Expected, Step}, + metadata::{deserialize_compilation_modes, serialize_compilation_modes}, mode::Mode, }; @@ -15,8 +18,13 @@ pub struct Case { #[serde(skip_serializing_if = "Option::is_none")] pub comment: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub modes: Option>, + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_compilation_modes", + serialize_with = "serialize_compilation_modes" + )] + pub modes: Option>, #[serde(rename = "inputs")] pub steps: Vec, @@ -32,7 +40,6 @@ pub struct Case { } impl Case { - #[allow(irrefutable_let_patterns)] pub fn steps_iterator(&self) -> impl Iterator { let steps_len = self.steps.len(); self.steps diff --git a/crates/format/src/metadata.rs b/crates/format/src/metadata.rs index 2a21c73..04d6178 100644 --- a/crates/format/src/metadata.rs +++ b/crates/format/src/metadata.rs @@ -1,6 +1,6 @@ use std::{ cmp::Ordering, - collections::BTreeMap, + collections::{BTreeMap, HashSet}, fmt::Display, fs::{File, read_to_string}, ops::Deref, @@ -8,7 +8,7 @@ use std::{ str::FromStr, }; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use revive_common::EVMVersion; use revive_dt_common::{iterators::FilesWithExtensionIterator, macros::define_wrapper_type}; @@ -65,8 +65,13 @@ pub struct Metadata { #[serde(skip_serializing_if = "Option::is_none")] pub libraries: Option>>, - #[serde(skip_serializing_if = "Option::is_none")] - pub modes: Option>, + #[serde( + default, + skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_compilation_modes", + serialize_with = "serialize_compilation_modes" + )] + pub modes: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub file_path: Option, @@ -88,7 +93,7 @@ impl Metadata { pub fn solc_modes(&self) -> Vec { self.modes .to_owned() - .unwrap_or_else(|| vec![Mode::Solidity(Default::default())]) + .unwrap_or_else(|| SolcMode::ALL.map(Mode::Solidity).iter().cloned().collect()) .iter() .filter_map(|mode| match mode { Mode::Solidity(solc_mode) => Some(solc_mode), @@ -265,6 +270,37 @@ impl Metadata { } } +pub fn deserialize_compilation_modes<'de, D>( + deserializer: D, +) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + let maybe_strings = Option::>::deserialize(deserializer)?; + Ok(maybe_strings.map(|strings| { + strings + .into_iter() + .flat_map(Mode::parse_from_string) + .collect() + })) +} + +pub fn serialize_compilation_modes( + value: &Option>, + serializer: S, +) -> Result +where + S: Serializer, +{ + match value { + None => serializer.serialize_none(), + Some(modes) => { + let strings: Vec = modes.iter().cloned().map(Into::::into).collect(); + serializer.serialize_some(&strings) + } + } +} + define_wrapper_type!( /// Represents a contract instance found a metadata file. /// diff --git a/crates/format/src/mode.rs b/crates/format/src/mode.rs index 8b1f4c0..e8ffb93 100644 --- a/crates/format/src/mode.rs +++ b/crates/format/src/mode.rs @@ -1,123 +1,393 @@ +use std::{fmt::Display, str::FromStr}; + use revive_dt_common::types::VersionOrRequirement; use semver::Version; -use serde::de::Deserializer; -use serde::{Deserialize, Serialize}; +use serde::Serialize; -/// Specifies the compilation mode of the test artifact. -#[derive(Hash, Debug, Clone, Eq, PartialEq)] +/// Specifies a compilation mode for the test artifact that it requires. This is used as a filter +/// when used in the [`Metadata`] and is used as a directive when used in the core crate. +/// +/// [`Metadata`]: crate::metadata::Metadata +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Mode { + /// A compilation mode that's been parsed from a String and into its contents. Solidity(SolcMode), + /// An unknown compilation mode. Unknown(String), } -/// Specify Solidity specific compiler options. -#[derive(Hash, Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] +impl From for String { + fn from(value: Mode) -> Self { + value.to_string() + } +} + +impl Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Mode::Solidity(mode) => mode.fmt(f), + Mode::Unknown(string) => string.fmt(f), + } + } +} + +impl Mode { + pub fn parse_from_string(str: impl AsRef) -> Vec { + let mut chars = str.as_ref().chars().peekable(); + + let compile_via_ir = match chars.next() { + Some('Y') => true, + Some('E') => false, + _ => { + tracing::warn!("Encountered an unknown mode {}", str.as_ref()); + return vec![Self::Unknown(str.as_ref().to_string())]; + } + }; + + let optimize_flag = match chars.peek() { + Some('+') => { + let _ = chars.next(); + Some(true) + } + Some('-') => { + let _ = chars.next(); + Some(false) + } + _ => None, + }; + + let mut chars = chars.skip_while(|char| *char == ' ').peekable(); + + let version_requirement = match chars.peek() { + Some('=' | '>' | '<' | '~' | '^' | '*' | '0'..='9') => { + let version_requirement = chars.take_while(|char| *char != ' ').collect::(); + let Ok(version_requirement) = VersionOrRequirement::from_str(&version_requirement) + else { + return vec![Self::Unknown(str.as_ref().to_string())]; + }; + Some(version_requirement) + } + _ => None, + }; + + match optimize_flag { + Some(flag) => { + vec![Self::Solidity(SolcMode { + via_ir: compile_via_ir, + optimize: flag, + compiler_version_requirement: version_requirement, + })] + } + None => { + vec![ + Self::Solidity(SolcMode { + via_ir: compile_via_ir, + optimize: true, + compiler_version_requirement: version_requirement.clone(), + }), + Self::Solidity(SolcMode { + via_ir: compile_via_ir, + optimize: false, + compiler_version_requirement: version_requirement, + }), + ] + } + } + } + + pub fn matches(&self, other: &Self) -> bool { + match (self, other) { + ( + Mode::Solidity(SolcMode { + via_ir: self_via_ir, + optimize: self_optimize, + compiler_version_requirement: self_compiler_version_requirement, + }), + Mode::Solidity(SolcMode { + via_ir: other_via_ir, + optimize: other_optimize, + compiler_version_requirement: other_compiler_version_requirement, + }), + ) => { + let mut matches = true; + matches &= self_via_ir == other_via_ir; + matches &= self_optimize == other_optimize; + match ( + self_compiler_version_requirement, + other_compiler_version_requirement, + ) { + ( + Some(VersionOrRequirement::Version(self_version)), + Some(VersionOrRequirement::Version(other_version)), + ) => { + matches &= self_version == other_version; + } + ( + Some(VersionOrRequirement::Version(version)), + Some(VersionOrRequirement::Requirement(requirement)), + ) + | ( + Some(VersionOrRequirement::Requirement(requirement)), + Some(VersionOrRequirement::Version(version)), + ) => matches &= requirement.matches(version), + ( + Some(VersionOrRequirement::Requirement(..)), + Some(VersionOrRequirement::Requirement(..)), + ) => matches = false, + (Some(_), None) | (None, Some(_)) | (None, None) => {} + } + matches + } + (Mode::Solidity { .. }, Mode::Unknown(_)) + | (Mode::Unknown(_), Mode::Solidity { .. }) + | (Mode::Unknown(_), Mode::Unknown(_)) => false, + } + } + + pub fn as_solc_mode(&self) -> Option<&SolcMode> { + if let Self::Solidity(mode) = self { + Some(mode) + } else { + None + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +#[serde(into = "String")] pub struct SolcMode { - pub solc_version: Option, - solc_optimize: Option, - pub llvm_optimizer_settings: Vec, - mode_string: String, + pub via_ir: bool, + pub optimize: bool, + pub compiler_version_requirement: Option, } impl SolcMode { - /// Try to parse a mode string into a solc mode. - /// Returns `None` if the string wasn't a solc YUL mode string. - /// - /// The mode string is expected to start with the `Y` ID (YUL ID), - /// optionally followed by `+` or `-` for the solc optimizer settings. - /// - /// Options can be separated by a whitespace contain the following - /// - A solc `SemVer version requirement` string - /// - One or more `-OX` where X is a supposed to be an LLVM opt mode - pub fn parse_from_mode_string(mode_string: &str) -> Option { - let mut result = Self { - mode_string: mode_string.to_string(), - ..Default::default() - }; + pub const ALL: [Self; 4] = [ + SolcMode { + via_ir: false, + optimize: false, + compiler_version_requirement: None, + }, + SolcMode { + via_ir: false, + optimize: true, + compiler_version_requirement: None, + }, + SolcMode { + via_ir: true, + optimize: false, + compiler_version_requirement: None, + }, + SolcMode { + via_ir: true, + optimize: true, + compiler_version_requirement: None, + }, + ]; - let mut parts = mode_string.trim().split(" "); - - match parts.next()? { - "Y" => {} - "Y+" => result.solc_optimize = Some(true), - "Y-" => result.solc_optimize = Some(false), - _ => return None, - } - - for part in parts { - if let Ok(solc_version) = semver::VersionReq::parse(part) { - result.solc_version = Some(solc_version); - continue; - } - if let Some(level) = part.strip_prefix("-O") { - result.llvm_optimizer_settings.push(level.to_string()); - continue; - } - panic!("the YUL mode string {mode_string} failed to parse, invalid part: {part}") - } - - Some(result) - } - - /// Returns whether to enable the solc optimizer. - pub fn solc_optimize(&self) -> bool { - self.solc_optimize.unwrap_or(true) - } - - /// Calculate the latest matching solc patch version. Returns: - /// - `latest_supported` if no version request was specified. - /// - A matching version with the same minor version as `latest_supported`, if any. - /// - `None` if no minor version of the `latest_supported` version matches. - pub fn last_patch_version(&self, latest_supported: &Version) -> Option { - let Some(version_req) = self.solc_version.as_ref() else { - return Some(latest_supported.to_owned()); - }; - - // lgtm - for patch in (0..latest_supported.patch + 1).rev() { - let version = Version::new(0, latest_supported.minor, patch); - if version_req.matches(&version) { - return Some(version); - } - } - - None + pub fn matches(&self, other: &Self) -> bool { + Mode::Solidity(self.clone()).matches(&Mode::Solidity(other.clone())) } /// Resolves the [`SolcMode`]'s solidity version requirement into a [`VersionOrRequirement`] if /// the requirement is present on the object. Otherwise, the passed default version is used. pub fn compiler_version_to_use(&self, default: Version) -> VersionOrRequirement { - match self.solc_version { - Some(ref requirement) => requirement.clone().into(), + match self.compiler_version_requirement { + Some(ref requirement) => requirement.clone(), None => default.into(), } } } -impl<'de> Deserialize<'de> for Mode { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let mode_string = String::deserialize(deserializer)?; +impl Display for SolcMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self { + via_ir, + optimize, + compiler_version_requirement, + } = self; - if let Some(solc_mode) = SolcMode::parse_from_mode_string(&mode_string) { - return Ok(Self::Solidity(solc_mode)); + if *via_ir { + write!(f, "Y")?; + } else { + write!(f, "E")?; } - Ok(Self::Unknown(mode_string)) + if *optimize { + write!(f, "+")?; + } else { + write!(f, "-")?; + } + + if let Some(req) = compiler_version_requirement { + write!(f, " {req}")?; + } + + Ok(()) } } -impl Serialize for Mode { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let string = match self { - Mode::Solidity(solc_mode) => &solc_mode.mode_string, - Mode::Unknown(string) => string, - }; - string.serialize(serializer) +impl From for String { + fn from(value: SolcMode) -> Self { + value.to_string() + } +} + +#[cfg(test)] +mod test { + use semver::Version; + + use super::*; + + #[test] + fn mode_can_be_parsed_as_expected() { + // Arrange + let fixtures = [ + ( + "Y", + vec![ + Mode::Solidity(SolcMode { + via_ir: true, + optimize: true, + compiler_version_requirement: None, + }), + Mode::Solidity(SolcMode { + via_ir: true, + optimize: false, + compiler_version_requirement: None, + }), + ], + ), + ( + "Y+", + vec![Mode::Solidity(SolcMode { + via_ir: true, + optimize: true, + compiler_version_requirement: None, + })], + ), + ( + "Y-", + vec![Mode::Solidity(SolcMode { + via_ir: true, + optimize: false, + compiler_version_requirement: None, + })], + ), + ( + "E", + vec![ + Mode::Solidity(SolcMode { + via_ir: false, + optimize: true, + compiler_version_requirement: None, + }), + Mode::Solidity(SolcMode { + via_ir: false, + optimize: false, + compiler_version_requirement: None, + }), + ], + ), + ( + "E+", + vec![Mode::Solidity(SolcMode { + via_ir: false, + optimize: true, + compiler_version_requirement: None, + })], + ), + ( + "E-", + vec![Mode::Solidity(SolcMode { + via_ir: false, + optimize: false, + compiler_version_requirement: None, + })], + ), + ( + "Y >=0.8.3", + vec![ + Mode::Solidity(SolcMode { + via_ir: true, + optimize: true, + compiler_version_requirement: Some(VersionOrRequirement::Requirement( + ">=0.8.3".parse().unwrap(), + )), + }), + Mode::Solidity(SolcMode { + via_ir: true, + optimize: false, + compiler_version_requirement: Some(VersionOrRequirement::Requirement( + ">=0.8.3".parse().unwrap(), + )), + }), + ], + ), + ( + "Y 0.8.3", + vec![ + Mode::Solidity(SolcMode { + via_ir: true, + optimize: true, + compiler_version_requirement: Some(VersionOrRequirement::Version( + Version { + major: 0, + minor: 8, + patch: 3, + pre: Default::default(), + build: Default::default(), + }, + )), + }), + Mode::Solidity(SolcMode { + via_ir: true, + optimize: false, + compiler_version_requirement: Some(VersionOrRequirement::Version( + Version { + major: 0, + minor: 8, + patch: 3, + pre: Default::default(), + build: Default::default(), + }, + )), + }), + ], + ), + ]; + + for (string, expectation) in fixtures { + // Act + let actual = Mode::parse_from_string(string); + + // Assert + assert_eq!( + actual, expectation, + "Parsed {string} into {actual:?} but expected {expectation:?}" + ) + } + } + + #[test] + #[allow(clippy::uninlined_format_args)] + fn mode_matches_as_expected() { + // Arrange + let fixtures = [("Y+", "Y+", true), ("Y+ >=0.8.3", "Y+", true)]; + + for (self_mode, other_mode, expected_result) in fixtures { + let self_mode = Mode::parse_from_string(self_mode).pop().unwrap(); + let other_mode = Mode::parse_from_string(other_mode).pop().unwrap(); + + // Act + let actual = self_mode.matches(&other_mode); + + // Assert + assert_eq!( + actual, expected_result, + "Match of {} and {} failed. Expected {} but got {}", + self_mode, other_mode, expected_result, actual + ); + } } } diff --git a/crates/report/src/reporter.rs b/crates/report/src/reporter.rs index 9b9303d..eb8e209 100644 --- a/crates/report/src/reporter.rs +++ b/crates/report/src/reporter.rs @@ -13,7 +13,7 @@ use std::{ use anyhow::Context; use revive_dt_compiler::{CompilerInput, CompilerOutput}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use revive_dt_config::{Arguments, TestingPlatform}; use revive_dt_format::{corpus::Corpus, mode::SolcMode}; @@ -23,7 +23,7 @@ use crate::analyzer::CompilerStatistics; pub(crate) static REPORTER: OnceLock> = OnceLock::new(); /// The `Report` datastructure stores all relevant inforamtion required for generating reports. -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize)] pub struct Report { /// The configuration used during the test. pub config: Arguments, @@ -41,7 +41,7 @@ pub struct Report { } /// Contains a compiled contract. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize)] pub struct CompilationTask { /// The observed compiler input. pub json_input: CompilerInput, @@ -56,7 +56,7 @@ pub struct CompilationTask { } /// Represents a report about a compilation task. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize)] pub struct CompilationResult { /// The observed compilation task. pub compilation_task: CompilationTask, @@ -65,7 +65,7 @@ pub struct CompilationResult { } /// The [Span] struct indicates the context of what is being reported. -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize)] pub struct Span { /// The corpus index this belongs to. corpus: usize,