remove support for legacy evm assembly (#186)

This commit is contained in:
xermicus
2025-02-03 14:13:43 +01:00
committed by GitHub
parent ab90af49df
commit bfda465c32
70 changed files with 325 additions and 5927 deletions
+3 -15
View File
@@ -1,7 +1,6 @@
//! The Solidity compiler.
pub mod combined_json;
pub mod pipeline;
#[cfg(not(target_os = "emscripten"))]
pub mod solc_compiler;
#[cfg(target_os = "emscripten")]
@@ -9,35 +8,25 @@ pub mod soljson_compiler;
pub mod standard_json;
pub mod version;
use once_cell::sync::Lazy;
use semver::VersionReq;
use std::path::Path;
use std::path::PathBuf;
use self::combined_json::CombinedJson;
use self::pipeline::Pipeline;
use self::standard_json::input::Input as StandardJsonInput;
use self::standard_json::output::Output as StandardJsonOutput;
use self::version::Version;
/// The first version of `solc` with the support of standard JSON interface.
pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 4, 12);
/// The first version of `solc`, where Yul codegen is considered robust enough.
pub const FIRST_YUL_VERSION: semver::Version = semver::Version::new(0, 8, 0);
pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0);
/// The first version of `solc`, where `--via-ir` codegen mode is supported.
pub const FIRST_VIA_IR_VERSION: semver::Version = semver::Version::new(0, 8, 13);
/// The last supported version of `solc`.
pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 28);
/// `--base-path` was introduced in 0.6.9 <https://github.com/ethereum/solidity/releases/tag/v0.6.9>
pub static FIRST_SUPPORTS_BASE_PATH: Lazy<VersionReq> =
Lazy::new(|| VersionReq::parse(">=0.6.9").unwrap());
/// `--include-path` was introduced in 0.8.8 <https://github.com/ethereum/solidity/releases/tag/v0.8.8>
pub static FIRST_SUPPORTS_INCLUDE_PATH: Lazy<VersionReq> =
Lazy::new(|| VersionReq::parse(">=0.8.8").unwrap());
/// `--include-path` was introduced in solc `0.8.8` <https://github.com/ethereum/solidity/releases/tag/v0.8.8>
pub const FIRST_INCLUDE_PATH_VERSION: semver::Version = semver::Version::new(0, 8, 8);
/// The Solidity compiler.
pub trait Compiler {
@@ -45,7 +34,6 @@ pub trait Compiler {
fn standard_json(
&mut self,
input: StandardJsonInput,
pipeline: Pipeline,
base_path: Option<String>,
include_paths: Vec<String>,
allow_paths: Option<String>,
-27
View File
@@ -1,27 +0,0 @@
//! The Solidity compiler pipeline type.
use serde::{Deserialize, Serialize};
use crate::solc::version::Version as SolcVersion;
/// The Solidity compiler pipeline type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
pub enum Pipeline {
/// The Yul IR.
Yul,
/// The EVM legacy assembly IR.
EVMLA,
}
impl Pipeline {
/// We always use EVMLA for Solidity <=0.7, or if the user does not want to compile via Yul.
pub fn new(solc_version: &SolcVersion, force_evmla: bool) -> Self {
if solc_version.default < crate::solc::FIRST_YUL_VERSION || force_evmla {
Self::EVMLA
} else {
Self::Yul
}
}
}
+12 -23
View File
@@ -5,13 +5,12 @@ use std::path::Path;
use std::path::PathBuf;
use crate::solc::combined_json::CombinedJson;
use crate::solc::pipeline::Pipeline;
use crate::solc::standard_json::input::Input as StandardJsonInput;
use crate::solc::standard_json::output::Output as StandardJsonOutput;
use crate::solc::version::Version;
use crate::solc::FIRST_INCLUDE_PATH_VERSION;
use super::Compiler;
use crate::solc::{FIRST_SUPPORTS_BASE_PATH, FIRST_SUPPORTS_INCLUDE_PATH};
/// The Solidity compiler.
pub struct SolcCompiler {
@@ -47,45 +46,35 @@ impl Compiler for SolcCompiler {
fn standard_json(
&mut self,
mut input: StandardJsonInput,
pipeline: Pipeline,
base_path: Option<String>,
include_paths: Vec<String>,
allow_paths: Option<String>,
) -> anyhow::Result<StandardJsonOutput> {
let version = self.version()?;
let version = self.version()?.default;
if !include_paths.is_empty() && version < FIRST_INCLUDE_PATH_VERSION {
anyhow::bail!("--include-path is not supported in solc {version}");
}
let mut command = std::process::Command::new(self.executable.as_str());
command.stdin(std::process::Stdio::piped());
command.stdout(std::process::Stdio::piped());
command.arg("--standard-json");
if let Some(base_path) = &base_path {
if !FIRST_SUPPORTS_BASE_PATH.matches(&version.default) {
anyhow::bail!(
"--base-path not supported this version {} of solc",
&version.default
);
}
command.arg("--base-path").arg(base_path);
}
if !include_paths.is_empty() && !FIRST_SUPPORTS_INCLUDE_PATH.matches(&version.default) {
anyhow::bail!(
"--include-path not supported this version {} of solc",
&version.default
);
}
for include_path in include_paths.into_iter() {
command.arg("--include-path");
command.arg(include_path);
}
if let Some(base_path) = base_path {
command.arg("--base-path");
command.arg(base_path);
}
if let Some(allow_paths) = allow_paths {
command.arg("--allow-paths");
command.arg(allow_paths);
}
input.normalize(&version.default);
input.normalize(&version);
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
@@ -129,7 +118,7 @@ impl Compiler for SolcCompiler {
),
)
})?;
output.preprocess_ast(&version, pipeline, suppressed_warnings.as_slice())?;
output.preprocess_ast(suppressed_warnings.as_slice())?;
Ok(output)
}
+1 -3
View File
@@ -4,7 +4,6 @@ use std::path::Path;
use std::path::PathBuf;
use crate::solc::combined_json::CombinedJson;
use crate::solc::pipeline::Pipeline;
use crate::solc::standard_json::input::Input as StandardJsonInput;
use crate::solc::standard_json::output::Output as StandardJsonOutput;
use crate::solc::version::Version;
@@ -29,7 +28,6 @@ impl Compiler for SoljsonCompiler {
fn standard_json(
&mut self,
mut input: StandardJsonInput,
pipeline: Pipeline,
_base_path: Option<String>,
_include_paths: Vec<String>,
_allow_paths: Option<String>,
@@ -50,7 +48,7 @@ impl Compiler for SoljsonCompiler {
.unwrap_or_else(|_| String::from_utf8_lossy(out.as_bytes()).to_string()),
)
})?;
output.preprocess_ast(&version, pipeline, suppressed_warnings.as_slice())?;
output.preprocess_ast(suppressed_warnings.as_slice())?;
Ok(output)
}
@@ -13,7 +13,6 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};
use serde::Deserialize;
use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use crate::solc::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
@@ -40,13 +39,13 @@ pub struct Input {
impl Input {
/// A shortcut constructor from stdin.
pub fn try_from_stdin(solc_pipeline: SolcPipeline) -> anyhow::Result<Self> {
pub fn try_from_stdin() -> anyhow::Result<Self> {
let mut input: Self = serde_json::from_reader(std::io::BufReader::new(std::io::stdin()))?;
input
.settings
.output_selection
.get_or_insert_with(SolcStandardJsonInputSettingsSelection::default)
.extend_with_required(solc_pipeline);
.extend_with_required();
Ok(input)
}
@@ -61,7 +60,6 @@ impl Input {
output_selection: SolcStandardJsonInputSettingsSelection,
optimizer: SolcStandardJsonInputSettingsOptimizer,
metadata: Option<SolcStandardJsonInputSettingsMetadata>,
via_ir: bool,
suppressed_warnings: Option<Vec<Warning>>,
) -> anyhow::Result<Self> {
let mut paths: BTreeSet<PathBuf> = paths.iter().cloned().collect();
@@ -92,7 +90,6 @@ impl Input {
libraries,
remappings,
output_selection,
via_ir,
optimizer,
metadata,
),
@@ -111,7 +108,6 @@ impl Input {
output_selection: SolcStandardJsonInputSettingsSelection,
optimizer: SolcStandardJsonInputSettingsOptimizer,
metadata: Option<SolcStandardJsonInputSettingsMetadata>,
via_ir: bool,
suppressed_warnings: Option<Vec<Warning>>,
) -> anyhow::Result<Self> {
#[cfg(feature = "parallel")]
@@ -131,7 +127,6 @@ impl Input {
libraries,
remappings,
output_selection,
via_ir,
optimizer,
metadata,
),
@@ -51,7 +51,6 @@ impl Settings {
libraries: BTreeMap<String, BTreeMap<String, String>>,
remappings: Option<BTreeSet<String>>,
output_selection: Selection,
via_ir: bool,
optimizer: Optimizer,
metadata: Option<Metadata>,
) -> Self {
@@ -60,9 +59,9 @@ impl Settings {
libraries: Some(libraries),
remappings,
output_selection: Some(output_selection),
via_ir: if via_ir { Some(true) } else { None },
optimizer,
metadata,
via_ir: Some(true),
}
}
@@ -3,8 +3,6 @@
use serde::Deserialize;
use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
/// The `solc --standard-json` expected output selection flag.
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
#[allow(non_camel_case_types)]
@@ -46,15 +44,6 @@ pub enum Flag {
Assembly,
}
impl From<SolcPipeline> for Flag {
fn from(pipeline: SolcPipeline) -> Self {
match pipeline {
SolcPipeline::Yul => Self::Yul,
SolcPipeline::EVMLA => Self::EVMLA,
}
}
}
impl std::fmt::Display for Flag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
@@ -7,8 +7,6 @@ use std::collections::HashSet;
use serde::Deserialize;
use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use self::flag::Flag as SelectionFlag;
/// The `solc --standard-json` output file selection.
@@ -24,7 +22,7 @@ pub struct File {
impl File {
/// Creates the selection required by our compilation process.
pub fn new_required(pipeline: SolcPipeline) -> Self {
pub fn new_required() -> Self {
Self {
per_file: Some(HashSet::from_iter([SelectionFlag::AST])),
per_contract: Some(HashSet::from_iter([
@@ -32,14 +30,14 @@ impl File {
SelectionFlag::EVMDBC,
SelectionFlag::MethodIdentifiers,
SelectionFlag::Metadata,
SelectionFlag::from(pipeline),
SelectionFlag::Yul,
])),
}
}
/// Extends the user's output selection with flag required by our compilation process.
pub fn extend_with_required(&mut self, pipeline: SolcPipeline) -> &mut Self {
let required = Self::new_required(pipeline);
pub fn extend_with_required(&mut self) -> &mut Self {
let required = Self::new_required();
self.per_file
.get_or_insert_with(HashSet::default)
@@ -5,8 +5,6 @@ pub mod file;
use serde::Deserialize;
use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use self::file::File as FileSelection;
/// The `solc --standard-json` output selection.
@@ -19,17 +17,17 @@ pub struct Selection {
impl Selection {
/// Creates the selection required by our compilation process.
pub fn new_required(pipeline: SolcPipeline) -> Self {
pub fn new_required() -> Self {
Self {
all: Some(FileSelection::new_required(pipeline)),
all: Some(FileSelection::new_required()),
}
}
/// Extends the user's output selection with flag required by our compilation process.
pub fn extend_with_required(&mut self, pipeline: SolcPipeline) -> &mut Self {
pub fn extend_with_required(&mut self) -> &mut Self {
self.all
.get_or_insert_with(|| FileSelection::new_required(pipeline))
.extend_with_required(pipeline);
.get_or_insert_with(FileSelection::new_required)
.extend_with_required();
self
}
}
@@ -1,46 +0,0 @@
//! The `solc --standard-json` output contract EVM extra metadata.
pub mod recursive_function;
use serde::Deserialize;
use serde::Serialize;
use self::recursive_function::RecursiveFunction;
/// The `solc --standard-json` output contract EVM extra metadata.
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ExtraMetadata {
/// The list of recursive functions.
#[serde(default = "Vec::new")]
pub recursive_functions: Vec<RecursiveFunction>,
}
impl ExtraMetadata {
/// Returns the recursive function reference for the specified tag.
pub fn get(
&self,
block_key: &revive_llvm_context::PolkaVMFunctionBlockKey,
) -> Option<&RecursiveFunction> {
for function in self.recursive_functions.iter() {
match block_key.code_type {
revive_llvm_context::PolkaVMCodeType::Deploy => {
if let Some(creation_tag) = function.creation_tag {
if num::BigUint::from(creation_tag) == block_key.tag {
return Some(function);
}
}
}
revive_llvm_context::PolkaVMCodeType::Runtime => {
if let Some(runtime_tag) = function.runtime_tag {
if num::BigUint::from(runtime_tag) == block_key.tag {
return Some(function);
}
}
}
}
}
None
}
}
@@ -1,22 +0,0 @@
//! The `solc --standard-json` output contract EVM recursive function.
use serde::Deserialize;
use serde::Serialize;
/// The `solc --standard-json` output contract EVM recursive function.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RecursiveFunction {
/// The function name.
pub name: String,
/// The creation code function block tag.
pub creation_tag: Option<usize>,
/// The runtime code function block tag.
pub runtime_tag: Option<usize>,
/// The number of input arguments.
#[serde(rename = "totalParamSize")]
pub input_size: usize,
/// The number of output arguments.
#[serde(rename = "totalRetParamSize")]
pub output_size: usize,
}
@@ -1,27 +1,20 @@
//! The `solc --standard-json` output contract EVM data.
pub mod bytecode;
pub mod extra_metadata;
use std::collections::BTreeMap;
use serde::Deserialize;
use serde::Serialize;
use crate::evmla::assembly::Assembly;
use self::bytecode::Bytecode;
use self::bytecode::DeployedBytecode;
use self::extra_metadata::ExtraMetadata;
/// The `solc --standard-json` output contract EVM data.
/// It is replaced by PolkaVM data after compiling.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct EVM {
/// The contract EVM legacy assembly code.
#[serde(rename = "legacyAssembly", skip_serializing_if = "Option::is_none")]
pub assembly: Option<Assembly>,
/// The contract PolkaVM assembly code.
#[serde(rename = "assembly", skip_serializing_if = "Option::is_none")]
pub assembly_text: Option<String>,
@@ -37,9 +30,6 @@ pub struct EVM {
/// The contract function signatures.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub method_identifiers: Option<BTreeMap<String, String>>,
/// The extra EVMLA metadata.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_metadata: Option<ExtraMetadata>,
}
impl EVM {
@@ -125,26 +125,6 @@ impl Error {
}
}
/// Returns the internal function pointer usage error.
pub fn message_internal_function_pointer(src: Option<&str>) -> Self {
let message = r#"
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Error: Internal function pointers are not supported in EVM legacy assembly pipeline. │
│ Please use the Yul IR codegen instead. │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘"#
.to_owned();
Self {
component: "general".to_owned(),
error_code: None,
formatted_message: message.clone(),
message,
severity: "error".to_owned(),
source_location: src.map(SourceLocation::from_str).and_then(Result::ok),
r#type: "Error".to_owned(),
}
}
/// Appends the contract path to the message..
pub fn push_contract_path(&mut self, path: &str) {
self.formatted_message
@@ -10,12 +10,9 @@ use serde::Deserialize;
use serde::Serialize;
use sha3::Digest;
use crate::evmla::assembly::instruction::Instruction;
use crate::evmla::assembly::Assembly;
use crate::project::contract::ir::IR as ProjectContractIR;
use crate::project::contract::Contract as ProjectContract;
use crate::project::Project;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use crate::solc::version::Version as SolcVersion;
use crate::warning::Warning;
use crate::yul::lexer::Lexer;
@@ -53,14 +50,9 @@ impl Output {
&mut self,
source_code_files: BTreeMap<String, String>,
libraries: BTreeMap<String, BTreeMap<String, String>>,
pipeline: SolcPipeline,
solc_version: &SolcVersion,
debug_config: &revive_llvm_context::DebugConfig,
) -> anyhow::Result<Project> {
if let SolcPipeline::EVMLA = pipeline {
self.preprocess_dependencies()?;
}
let files = match self.contracts.as_ref() {
Some(files) => files,
None => match &self.errors {
@@ -76,38 +68,22 @@ impl Output {
for (name, contract) in contracts.iter() {
let full_path = format!("{path}:{name}");
let source = match pipeline {
SolcPipeline::Yul => {
let ir_optimized = match contract.ir_optimized.to_owned() {
Some(ir_optimized) => ir_optimized,
None => continue,
};
if ir_optimized.is_empty() {
continue;
}
debug_config.dump_yul(full_path.as_str(), ir_optimized.as_str())?;
let mut lexer = Lexer::new(ir_optimized.to_owned());
let object = Object::parse(&mut lexer, None).map_err(|error| {
anyhow::anyhow!("Contract `{}` parsing error: {:?}", full_path, error)
})?;
ProjectContractIR::new_yul(ir_optimized.to_owned(), object)
}
SolcPipeline::EVMLA => {
let evm = contract.evm.as_ref();
let assembly = match evm.and_then(|evm| evm.assembly.to_owned()) {
Some(assembly) => assembly.to_owned(),
None => continue,
};
let extra_metadata = evm
.and_then(|evm| evm.extra_metadata.to_owned())
.unwrap_or_default();
ProjectContractIR::new_evmla(assembly, extra_metadata)
}
let ir_optimized = match contract.ir_optimized.to_owned() {
Some(ir_optimized) => ir_optimized,
None => continue,
};
if ir_optimized.is_empty() {
continue;
}
debug_config.dump_yul(full_path.as_str(), ir_optimized.as_str())?;
let mut lexer = Lexer::new(ir_optimized.to_owned());
let object = Object::parse(&mut lexer, None).map_err(|error| {
anyhow::anyhow!("Contract `{}` parsing error: {:?}", full_path, error)
})?;
let source = ProjectContractIR::new_yul(ir_optimized.to_owned(), object);
let source_code = source_code_files
.get(path.as_str())
@@ -133,12 +109,7 @@ impl Output {
}
/// Traverses the AST and returns the list of additional errors and warnings.
pub fn preprocess_ast(
&mut self,
version: &SolcVersion,
pipeline: SolcPipeline,
suppressed_warnings: &[Warning],
) -> anyhow::Result<()> {
pub fn preprocess_ast(&mut self, suppressed_warnings: &[Warning]) -> anyhow::Result<()> {
let sources = match self.sources.as_ref() {
Some(sources) => sources,
None => return Ok(()),
@@ -147,8 +118,7 @@ impl Output {
let mut messages = Vec::new();
for (path, source) in sources.iter() {
if let Some(ast) = source.ast.as_ref() {
let mut polkavm_messages =
Source::get_messages(ast, version, pipeline, suppressed_warnings);
let mut polkavm_messages = Source::get_messages(ast, suppressed_warnings);
for message in polkavm_messages.iter_mut() {
message.push_contract_path(path.as_str());
}
@@ -165,83 +135,4 @@ impl Output {
Ok(())
}
/// The pass, which replaces with dependency indexes with actual data.
fn preprocess_dependencies(&mut self) -> anyhow::Result<()> {
let files = match self.contracts.as_mut() {
Some(files) => files,
None => return Ok(()),
};
let mut hash_path_mapping = BTreeMap::new();
for (path, contracts) in files.iter() {
for (name, contract) in contracts.iter() {
let full_path = format!("{path}:{name}");
let hash = match contract
.evm
.as_ref()
.and_then(|evm| evm.assembly.as_ref())
.map(|assembly| assembly.keccak256())
{
Some(hash) => hash,
None => continue,
};
hash_path_mapping.insert(hash, full_path);
}
}
for (path, contracts) in files.iter_mut() {
for (name, contract) in contracts.iter_mut() {
let assembly = match contract.evm.as_mut().and_then(|evm| evm.assembly.as_mut()) {
Some(assembly) => assembly,
None => continue,
};
let full_path = format!("{path}:{name}");
Self::preprocess_dependency_level(
full_path.as_str(),
assembly,
&hash_path_mapping,
)?;
}
}
Ok(())
}
/// Preprocesses an assembly JSON structure dependency data map.
fn preprocess_dependency_level(
full_path: &str,
assembly: &mut Assembly,
hash_path_mapping: &BTreeMap<String, String>,
) -> anyhow::Result<()> {
assembly.set_full_path(full_path.to_owned());
let deploy_code_index_path_mapping =
assembly.deploy_dependencies_pass(full_path, hash_path_mapping)?;
if let Some(deploy_code_instructions) = assembly.code.as_deref_mut() {
Instruction::replace_data_aliases(
deploy_code_instructions,
&deploy_code_index_path_mapping,
)?;
};
let runtime_code_index_path_mapping =
assembly.runtime_dependencies_pass(full_path, hash_path_mapping)?;
if let Some(runtime_code_instructions) = assembly
.data
.as_mut()
.and_then(|data_map| data_map.get_mut("0"))
.and_then(|data| data.get_assembly_mut())
.and_then(|assembly| assembly.code.as_deref_mut())
{
Instruction::replace_data_aliases(
runtime_code_instructions,
&runtime_code_index_path_mapping,
)?;
}
Ok(())
}
}
@@ -3,9 +3,7 @@
use serde::Deserialize;
use serde::Serialize;
use crate::solc::pipeline::Pipeline as SolcPipeline;
use crate::solc::standard_json::output::error::Error as SolcStandardJsonOutputError;
use crate::solc::version::Version as SolcVersion;
use crate::warning::Warning;
/// The `solc --standard-json` output source.
@@ -132,37 +130,9 @@ impl Source {
))
}
/// Checks the AST node for the internal function pointers value usage.
pub fn check_internal_function_pointer(
ast: &serde_json::Value,
) -> Option<SolcStandardJsonOutputError> {
let ast = ast.as_object()?;
if ast.get("nodeType")?.as_str()? != "VariableDeclaration" {
return None;
}
let type_descriptions = ast.get("typeDescriptions")?.as_object()?;
if !type_descriptions
.get("typeIdentifier")?
.as_str()?
.contains("function_internal")
{
return None;
}
Some(
SolcStandardJsonOutputError::message_internal_function_pointer(
ast.get("src")?.as_str(),
),
)
}
/// Returns the list of messages for some specific parts of the AST.
pub fn get_messages(
ast: &serde_json::Value,
version: &SolcVersion,
pipeline: SolcPipeline,
suppressed_warnings: &[Warning],
) -> Vec<SolcStandardJsonOutputError> {
let mut messages = Vec::new();
@@ -189,31 +159,16 @@ impl Source {
messages.push(message);
}
}
if SolcPipeline::EVMLA == pipeline && version.l2_revision.is_none() {
if let Some(message) = Self::check_internal_function_pointer(ast) {
messages.push(message);
}
}
match ast {
serde_json::Value::Array(array) => {
for element in array.iter() {
messages.extend(Self::get_messages(
element,
version,
pipeline,
suppressed_warnings,
));
messages.extend(Self::get_messages(element, suppressed_warnings));
}
}
serde_json::Value::Object(object) => {
for (_key, value) in object.iter() {
messages.extend(Self::get_messages(
value,
version,
pipeline,
suppressed_warnings,
));
messages.extend(Self::get_messages(value, suppressed_warnings));
}
}
_ => {}