Skip to content

Commit 034ecd6

Browse files
authored
feat: replace solang with solar (#215)
1 parent 21617d0 commit 034ecd6

File tree

12 files changed

+204
-290
lines changed

12 files changed

+204
-290
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ semver = { version = "1.0", features = ["serde"] }
5151
serde = { version = "1", features = ["derive", "rc"] }
5252
serde_json = "1.0"
5353
similar-asserts = "1"
54-
solang-parser = { version = "=0.3.3", default-features = false }
54+
solar-parse = { version = "=0.1.0", default-features = false }
5555
svm = { package = "svm-rs", version = "0.5", default-features = false }
5656
tempfile = "3.9"
5757
thiserror = "1"

crates/compilers/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ md-5.workspace = true
2828
thiserror.workspace = true
2929
path-slash.workspace = true
3030
yansi.workspace = true
31-
solang-parser.workspace = true
31+
solar-parse.workspace = true
3232
once_cell = { workspace = true, optional = true }
3333
futures-util = { workspace = true, optional = true }
3434
tokio = { workspace = true, optional = true }

crates/compilers/src/compilers/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,17 +136,25 @@ pub trait CompilerInput: Serialize + Send + Sync + Sized + Debug {
136136
pub trait ParsedSource: Debug + Sized + Send + Clone {
137137
type Language: Language;
138138

139+
/// Parses the content of the source file.
139140
fn parse(content: &str, file: &Path) -> Result<Self>;
141+
142+
/// Returns the version requirement of the source.
140143
fn version_req(&self) -> Option<&VersionReq>;
141144

145+
/// Returns a list of contract names defined in the source.
146+
fn contract_names(&self) -> &[String];
147+
148+
/// Returns the language of the source.
149+
fn language(&self) -> Self::Language;
150+
142151
/// Invoked during import resolution. Should resolve imports for the given source, and populate
143152
/// include_paths for compilers which support this config.
144153
fn resolve_imports<C>(
145154
&self,
146155
paths: &ProjectPathsConfig<C>,
147156
include_paths: &mut BTreeSet<PathBuf>,
148157
) -> Result<Vec<PathBuf>>;
149-
fn language(&self) -> Self::Language;
150158

151159
/// Used to configure [OutputSelection] for sparse builds. In certain cases, we might want to
152160
/// include some of the file dependencies into the compiler output even if we might not be

crates/compilers/src/compilers/multi.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -316,14 +316,10 @@ impl ParsedSource for MultiCompilerParsedSource {
316316
}
317317
}
318318

319-
fn resolve_imports<C>(
320-
&self,
321-
paths: &crate::ProjectPathsConfig<C>,
322-
include_paths: &mut BTreeSet<PathBuf>,
323-
) -> Result<Vec<PathBuf>> {
319+
fn contract_names(&self) -> &[String] {
324320
match self {
325-
Self::Solc(parsed) => parsed.resolve_imports(paths, include_paths),
326-
Self::Vyper(parsed) => parsed.resolve_imports(paths, include_paths),
321+
Self::Solc(parsed) => parsed.contract_names(),
322+
Self::Vyper(parsed) => parsed.contract_names(),
327323
}
328324
}
329325

@@ -334,6 +330,17 @@ impl ParsedSource for MultiCompilerParsedSource {
334330
}
335331
}
336332

333+
fn resolve_imports<C>(
334+
&self,
335+
paths: &crate::ProjectPathsConfig<C>,
336+
include_paths: &mut BTreeSet<PathBuf>,
337+
) -> Result<Vec<PathBuf>> {
338+
match self {
339+
Self::Solc(parsed) => parsed.resolve_imports(paths, include_paths),
340+
Self::Vyper(parsed) => parsed.resolve_imports(paths, include_paths),
341+
}
342+
}
343+
337344
fn compilation_dependencies<'a>(
338345
&self,
339346
imported_nodes: impl Iterator<Item = (&'a Path, &'a Self)>,

crates/compilers/src/compilers/solc/mod.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use foundry_compilers_artifacts::{
1212
Error, Settings, Severity, SolcInput,
1313
};
1414
use foundry_compilers_core::error::Result;
15-
use itertools::Itertools;
1615
use semver::Version;
1716
use serde::{Deserialize, Serialize};
1817
use std::{
@@ -260,12 +259,8 @@ impl ParsedSource for SolData {
260259
self.version_req.as_ref()
261260
}
262261

263-
fn resolve_imports<C>(
264-
&self,
265-
_paths: &crate::ProjectPathsConfig<C>,
266-
_include_paths: &mut BTreeSet<PathBuf>,
267-
) -> Result<Vec<PathBuf>> {
268-
Ok(self.imports.iter().map(|i| i.data().path().to_path_buf()).collect_vec())
262+
fn contract_names(&self) -> &[String] {
263+
&self.contract_names
269264
}
270265

271266
fn language(&self) -> Self::Language {
@@ -276,6 +271,14 @@ impl ParsedSource for SolData {
276271
}
277272
}
278273

274+
fn resolve_imports<C>(
275+
&self,
276+
_paths: &crate::ProjectPathsConfig<C>,
277+
_include_paths: &mut BTreeSet<PathBuf>,
278+
) -> Result<Vec<PathBuf>> {
279+
Ok(self.imports.iter().map(|i| i.data().path().to_path_buf()).collect())
280+
}
281+
279282
fn compilation_dependencies<'a>(
280283
&self,
281284
imported_nodes: impl Iterator<Item = (&'a Path, &'a Self)>,

crates/compilers/src/compilers/vyper/parser.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ impl ParsedSource for VyperParsedSource {
5252
self.version_req.as_ref()
5353
}
5454

55+
fn contract_names(&self) -> &[String] {
56+
&[]
57+
}
58+
59+
fn language(&self) -> Self::Language {
60+
VyperLanguage
61+
}
62+
5563
fn resolve_imports<C>(
5664
&self,
5765
paths: &ProjectPathsConfig<C>,
@@ -137,10 +145,6 @@ impl ParsedSource for VyperParsedSource {
137145
}
138146
Ok(imports)
139147
}
140-
141-
fn language(&self) -> Self::Language {
142-
VyperLanguage
143-
}
144148
}
145149

146150
/// Parses given source trying to find all import directives.

crates/compilers/src/config.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,36 +131,37 @@ impl ProjectPathsConfig<SolcLanguage> {
131131
let mut result = String::new();
132132

133133
for path in ordered_deps.iter() {
134-
let node_id = graph.files().get(path).ok_or_else(|| {
134+
let node_id = *graph.files().get(path).ok_or_else(|| {
135135
SolcError::msg(format!("cannot resolve file at {}", path.display()))
136136
})?;
137-
let node = graph.node(*node_id);
137+
let node = graph.node(node_id);
138+
node.data.parse_result()?;
138139
let content = node.content();
139140

140141
// Firstly we strip all licesnses, verson pragmas
141142
// We keep target file pragma and license placing them in the beginning of the result.
142143
let mut ranges_to_remove = Vec::new();
143144

144145
if let Some(license) = &node.data.license {
145-
ranges_to_remove.push(license.loc());
146+
ranges_to_remove.push(license.span());
146147
if *path == flatten_target {
147-
result.push_str(&content[license.loc()]);
148+
result.push_str(&content[license.span()]);
148149
result.push('\n');
149150
}
150151
}
151152
if let Some(version) = &node.data.version {
152-
let content = &content[version.loc()];
153-
ranges_to_remove.push(version.loc());
153+
let content = &content[version.span()];
154+
ranges_to_remove.push(version.span());
154155
version_pragmas.push(content);
155156
}
156157
if let Some(experimental) = &node.data.experimental {
157-
ranges_to_remove.push(experimental.loc());
158+
ranges_to_remove.push(experimental.span());
158159
if experimental_pragma.is_none() {
159-
experimental_pragma = Some(content[experimental.loc()].to_owned());
160+
experimental_pragma = Some(content[experimental.span()].to_owned());
160161
}
161162
}
162163
for import in &node.data.imports {
163-
ranges_to_remove.push(import.loc());
164+
ranges_to_remove.push(import.span());
164165
}
165166
ranges_to_remove.sort_by_key(|loc| loc.start);
166167

crates/compilers/src/lib.rs

Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,16 @@ use compile::output::contracts::VersionedContracts;
5050
use compilers::multi::MultiCompiler;
5151
use derivative::Derivative;
5252
use foundry_compilers_artifacts::solc::{
53-
output_selection::OutputSelection,
5453
sources::{Source, SourceCompilationKind, Sources},
5554
Contract, Severity, SourceFile, StandardJsonCompilerInput,
5655
};
5756
use foundry_compilers_core::error::{Result, SolcError, SolcIoError};
5857
use output::sources::{VersionedSourceFile, VersionedSourceFiles};
5958
use project::ProjectCompiler;
6059
use semver::Version;
61-
use solang_parser::pt::SourceUnitPart;
6260
use solc::SolcSettings;
6361
use std::{
6462
collections::{BTreeMap, HashMap, HashSet},
65-
fs,
6663
path::{Path, PathBuf},
6764
};
6865

@@ -361,62 +358,24 @@ impl<T: ArtifactOutput, C: Compiler> Project<C, T> {
361358
Ok(())
362359
}
363360

364-
/// Runs solc compiler without requesting any output and collects a mapping from contract names
365-
/// to source files containing artifact with given name.
366-
fn collect_contract_names_solc(&self) -> Result<HashMap<String, Vec<PathBuf>>>
367-
where
368-
T: Clone,
369-
C: Clone,
370-
{
371-
let mut temp_project = (*self).clone();
372-
temp_project.no_artifacts = true;
373-
temp_project.settings.update_output_selection(|selection| {
374-
*selection = OutputSelection::common_output_selection(["abi".to_string()]);
375-
});
376-
377-
let output = temp_project.compile()?;
378-
379-
if output.has_compiler_errors() {
380-
return Err(SolcError::msg(output));
381-
}
382-
383-
let contracts = output.into_artifacts().fold(
384-
HashMap::new(),
385-
|mut contracts: HashMap<_, Vec<_>>, (id, _)| {
386-
contracts.entry(id.name).or_default().push(id.source);
387-
contracts
388-
},
389-
);
390-
391-
Ok(contracts)
392-
}
393-
394-
/// Parses project sources via solang parser, collecting mapping from contract name to source
395-
/// files containing artifact with given name. On parser failure, fallbacks to
396-
/// [Self::collect_contract_names_solc].
361+
/// Parses the sources in memory and collects all the contract names mapped to their file paths.
397362
fn collect_contract_names(&self) -> Result<HashMap<String, Vec<PathBuf>>>
398363
where
399364
T: Clone,
400365
C: Clone,
401366
{
402367
let graph = Graph::<C::ParsedSource>::resolve(&self.paths)?;
403368
let mut contracts: HashMap<String, Vec<PathBuf>> = HashMap::new();
404-
405-
for file in graph.files().keys() {
406-
let src = fs::read_to_string(file).map_err(|e| SolcError::io(e, file))?;
407-
let Ok((parsed, _)) = solang_parser::parse(&src, 0) else {
408-
return self.collect_contract_names_solc();
409-
};
410-
411-
for part in parsed.0 {
412-
if let SourceUnitPart::ContractDefinition(contract) = part {
413-
if let Some(name) = contract.name {
414-
contracts.entry(name.name).or_default().push(file.clone());
415-
}
369+
if !graph.is_empty() {
370+
for node in graph.nodes(0) {
371+
for contract_name in node.data.contract_names() {
372+
contracts
373+
.entry(contract_name.clone())
374+
.or_default()
375+
.push(node.path().to_path_buf());
416376
}
417377
}
418378
}
419-
420379
Ok(contracts)
421380
}
422381

crates/compilers/src/resolver/mod.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,16 @@ impl<D: ParsedSource> Graph<D> {
240240
!self.edges.edges[index].is_empty()
241241
}
242242

243-
/// Returns all the resolved files and their index in the graph
243+
/// Returns all the resolved files and their index in the graph.
244244
pub fn files(&self) -> &HashMap<PathBuf, usize> {
245245
&self.edges.indices
246246
}
247247

248+
/// Returns `true` if the graph is empty.
249+
pub fn is_empty(&self) -> bool {
250+
self.nodes.is_empty()
251+
}
252+
248253
/// Gets a node by index.
249254
///
250255
/// # Panics
@@ -905,6 +910,12 @@ impl<D: ParsedSource> Node<D> {
905910
Ok(Self { path: file.to_path_buf(), source, data })
906911
}
907912

913+
/// Returns the path of the file.
914+
pub fn path(&self) -> &Path {
915+
&self.path
916+
}
917+
918+
/// Returns the contents of the file.
908919
pub fn content(&self) -> &str {
909920
&self.source.content
910921
}

0 commit comments

Comments
 (0)