1use super::{
2 restrictions::CompilerSettingsRestrictions,
3 solc::{SolcCompiler, SolcSettings, SolcVersionedInput, SOLC_EXTENSIONS},
4 vyper::{
5 input::VyperVersionedInput, parser::VyperParsedSource, Vyper, VyperLanguage,
6 VYPER_EXTENSIONS,
7 },
8 CompilationError, Compiler, CompilerInput, CompilerOutput, CompilerSettings, CompilerVersion,
9 Language, ParsedSource,
10};
11use crate::{
12 artifacts::vyper::{VyperCompilationError, VyperSettings},
13 parser::VyperParser,
14 resolver::parse::{SolData, SolParser},
15 settings::VyperRestrictions,
16 solc::SolcRestrictions,
17 SourceParser,
18};
19use foundry_compilers_artifacts::{
20 error::SourceLocation,
21 output_selection::OutputSelection,
22 remappings::Remapping,
23 sources::{Source, Sources},
24 Contract, Error, Severity, SolcLanguage,
25};
26use foundry_compilers_core::error::{Result, SolcError};
27use semver::Version;
28use serde::{Deserialize, Serialize};
29use std::{
30 borrow::Cow,
31 collections::BTreeSet,
32 fmt,
33 path::{Path, PathBuf},
34};
35
36#[derive(Clone, Debug)]
38pub struct MultiCompiler {
39 pub solc: Option<SolcCompiler>,
40 pub vyper: Option<Vyper>,
41}
42
43impl Default for MultiCompiler {
44 fn default() -> Self {
45 let vyper = Vyper::new("vyper").ok();
46
47 #[cfg(feature = "svm-solc")]
48 let solc = Some(SolcCompiler::AutoDetect);
49 #[cfg(not(feature = "svm-solc"))]
50 let solc = crate::solc::Solc::new("solc").map(SolcCompiler::Specific).ok();
51
52 Self { solc, vyper }
53 }
54}
55
56impl MultiCompiler {
57 pub fn new(solc: Option<SolcCompiler>, vyper_path: Option<PathBuf>) -> Result<Self> {
58 let vyper = vyper_path.map(Vyper::new).transpose()?;
59 Ok(Self { solc, vyper })
60 }
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
65#[serde(untagged)]
66pub enum MultiCompilerLanguage {
67 Solc(SolcLanguage),
68 Vyper(VyperLanguage),
69}
70
71impl Default for MultiCompilerLanguage {
72 fn default() -> Self {
73 Self::Solc(SolcLanguage::Solidity)
74 }
75}
76
77impl MultiCompilerLanguage {
78 pub fn is_vyper(&self) -> bool {
79 matches!(self, Self::Vyper(_))
80 }
81
82 pub fn is_solc(&self) -> bool {
83 matches!(self, Self::Solc(_))
84 }
85}
86
87impl From<SolcLanguage> for MultiCompilerLanguage {
88 fn from(language: SolcLanguage) -> Self {
89 Self::Solc(language)
90 }
91}
92
93impl From<VyperLanguage> for MultiCompilerLanguage {
94 fn from(language: VyperLanguage) -> Self {
95 Self::Vyper(language)
96 }
97}
98
99impl Language for MultiCompilerLanguage {
100 const FILE_EXTENSIONS: &'static [&'static str] = &["sol", "vy", "vyi", "yul"];
101}
102
103impl fmt::Display for MultiCompilerLanguage {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 match self {
106 Self::Solc(lang) => lang.fmt(f),
107 Self::Vyper(lang) => lang.fmt(f),
108 }
109 }
110}
111
112#[derive(Clone, Debug)]
114pub struct MultiCompilerParser {
115 solc: SolParser,
116 vyper: VyperParser,
117}
118
119impl MultiCompilerParser {
120 pub fn solc(&self) -> &SolParser {
122 &self.solc
123 }
124
125 pub fn solc_mut(&mut self) -> &mut SolParser {
127 &mut self.solc
128 }
129
130 pub fn vyper(&self) -> &VyperParser {
132 &self.vyper
133 }
134
135 pub fn vyper_mut(&mut self) -> &mut VyperParser {
137 &mut self.vyper
138 }
139}
140
141#[derive(Clone, Debug)]
143pub enum MultiCompilerParsedSource {
144 Solc(SolData),
145 Vyper(VyperParsedSource),
146}
147
148impl MultiCompilerParsedSource {
149 fn solc(&self) -> Option<&SolData> {
150 match self {
151 Self::Solc(parsed) => Some(parsed),
152 _ => None,
153 }
154 }
155
156 fn vyper(&self) -> Option<&VyperParsedSource> {
157 match self {
158 Self::Vyper(parsed) => Some(parsed),
159 _ => None,
160 }
161 }
162}
163
164#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
166#[serde(untagged)]
167pub enum MultiCompilerError {
168 Solc(Error),
169 Vyper(VyperCompilationError),
170}
171
172impl fmt::Display for MultiCompilerError {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 match self {
175 Self::Solc(error) => error.fmt(f),
176 Self::Vyper(error) => error.fmt(f),
177 }
178 }
179}
180
181#[derive(Clone, Copy, Debug, Default)]
182pub struct MultiCompilerRestrictions {
183 pub solc: SolcRestrictions,
184 pub vyper: VyperRestrictions,
185}
186
187impl CompilerSettingsRestrictions for MultiCompilerRestrictions {
188 fn merge(self, other: Self) -> Option<Self> {
189 Some(Self { solc: self.solc.merge(other.solc)?, vyper: self.vyper.merge(other.vyper)? })
190 }
191}
192
193#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
195pub struct MultiCompilerSettings {
196 pub solc: SolcSettings,
197 pub vyper: VyperSettings,
198}
199
200impl CompilerSettings for MultiCompilerSettings {
201 type Restrictions = MultiCompilerRestrictions;
202
203 fn can_use_cached(&self, other: &Self) -> bool {
204 self.solc.can_use_cached(&other.solc) && self.vyper.can_use_cached(&other.vyper)
205 }
206
207 fn update_output_selection(&mut self, f: impl FnOnce(&mut OutputSelection) + Copy) {
208 self.solc.update_output_selection(f);
209 self.vyper.update_output_selection(f);
210 }
211
212 fn with_allow_paths(self, allowed_paths: &BTreeSet<PathBuf>) -> Self {
213 Self {
214 solc: self.solc.with_allow_paths(allowed_paths),
215 vyper: self.vyper.with_allow_paths(allowed_paths),
216 }
217 }
218
219 fn with_base_path(self, base_path: &Path) -> Self {
220 Self {
221 solc: self.solc.with_base_path(base_path),
222 vyper: self.vyper.with_base_path(base_path),
223 }
224 }
225
226 fn with_include_paths(self, include_paths: &BTreeSet<PathBuf>) -> Self {
227 Self {
228 solc: self.solc.with_include_paths(include_paths),
229 vyper: self.vyper.with_include_paths(include_paths),
230 }
231 }
232
233 fn with_remappings(self, remappings: &[Remapping]) -> Self {
234 Self {
235 solc: self.solc.with_remappings(remappings),
236 vyper: self.vyper.with_remappings(remappings),
237 }
238 }
239
240 fn satisfies_restrictions(&self, restrictions: &Self::Restrictions) -> bool {
241 self.solc.satisfies_restrictions(&restrictions.solc)
242 && self.vyper.satisfies_restrictions(&restrictions.vyper)
243 }
244}
245
246impl From<MultiCompilerSettings> for SolcSettings {
247 fn from(settings: MultiCompilerSettings) -> Self {
248 settings.solc
249 }
250}
251
252impl From<MultiCompilerSettings> for VyperSettings {
253 fn from(settings: MultiCompilerSettings) -> Self {
254 settings.vyper
255 }
256}
257
258#[derive(Clone, Debug, Serialize)]
260#[serde(untagged)]
261pub enum MultiCompilerInput {
262 Solc(SolcVersionedInput),
263 Vyper(VyperVersionedInput),
264}
265
266impl CompilerInput for MultiCompilerInput {
267 type Language = MultiCompilerLanguage;
268 type Settings = MultiCompilerSettings;
269
270 fn build(
271 sources: Sources,
272 settings: Self::Settings,
273 language: Self::Language,
274 version: Version,
275 ) -> Self {
276 match language {
277 MultiCompilerLanguage::Solc(language) => {
278 Self::Solc(SolcVersionedInput::build(sources, settings.solc, language, version))
279 }
280 MultiCompilerLanguage::Vyper(language) => {
281 Self::Vyper(VyperVersionedInput::build(sources, settings.vyper, language, version))
282 }
283 }
284 }
285
286 fn compiler_name(&self) -> Cow<'static, str> {
287 match self {
288 Self::Solc(input) => input.compiler_name(),
289 Self::Vyper(input) => input.compiler_name(),
290 }
291 }
292
293 fn language(&self) -> Self::Language {
294 match self {
295 Self::Solc(input) => MultiCompilerLanguage::Solc(input.language()),
296 Self::Vyper(input) => MultiCompilerLanguage::Vyper(input.language()),
297 }
298 }
299
300 fn strip_prefix(&mut self, base: &Path) {
301 match self {
302 Self::Solc(input) => input.strip_prefix(base),
303 Self::Vyper(input) => input.strip_prefix(base),
304 }
305 }
306
307 fn version(&self) -> &Version {
308 match self {
309 Self::Solc(input) => input.version(),
310 Self::Vyper(input) => input.version(),
311 }
312 }
313
314 fn sources(&self) -> impl Iterator<Item = (&Path, &Source)> {
315 let ret: Box<dyn Iterator<Item = _>> = match self {
316 Self::Solc(input) => Box::new(input.sources()),
317 Self::Vyper(input) => Box::new(input.sources()),
318 };
319
320 ret
321 }
322}
323
324impl Compiler for MultiCompiler {
325 type Input = MultiCompilerInput;
326 type CompilationError = MultiCompilerError;
327 type Parser = MultiCompilerParser;
328 type Settings = MultiCompilerSettings;
329 type Language = MultiCompilerLanguage;
330 type CompilerContract = Contract;
331
332 fn compile(
333 &self,
334 input: &Self::Input,
335 ) -> Result<CompilerOutput<Self::CompilationError, Self::CompilerContract>> {
336 match input {
337 MultiCompilerInput::Solc(input) => {
338 if let Some(solc) = &self.solc {
339 Compiler::compile(solc, input).map(|res| res.map_err(MultiCompilerError::Solc))
340 } else {
341 Err(SolcError::msg("solc compiler is not available"))
342 }
343 }
344 MultiCompilerInput::Vyper(input) => {
345 if let Some(vyper) = &self.vyper {
346 Compiler::compile(vyper, input)
347 .map(|res| res.map_err(MultiCompilerError::Vyper))
348 } else {
349 Err(SolcError::msg("vyper compiler is not available"))
350 }
351 }
352 }
353 }
354
355 fn available_versions(&self, language: &Self::Language) -> Vec<CompilerVersion> {
356 match language {
357 MultiCompilerLanguage::Solc(language) => {
358 self.solc.as_ref().map(|s| s.available_versions(language)).unwrap_or_default()
359 }
360 MultiCompilerLanguage::Vyper(language) => {
361 self.vyper.as_ref().map(|v| v.available_versions(language)).unwrap_or_default()
362 }
363 }
364 }
365}
366
367impl SourceParser for MultiCompilerParser {
368 type ParsedSource = MultiCompilerParsedSource;
369
370 fn new(config: &crate::ProjectPathsConfig) -> Self {
371 Self { solc: SolParser::new(config), vyper: VyperParser::new(config) }
372 }
373
374 fn read(&mut self, path: &Path) -> Result<crate::resolver::Node<Self::ParsedSource>> {
375 Ok(match guess_lang(path)? {
376 MultiCompilerLanguage::Solc(_) => {
377 self.solc.read(path)?.map_data(MultiCompilerParsedSource::Solc)
378 }
379 MultiCompilerLanguage::Vyper(_) => {
380 self.vyper.read(path)?.map_data(MultiCompilerParsedSource::Vyper)
381 }
382 })
383 }
384
385 fn parse_sources(
386 &mut self,
387 sources: &mut Sources,
388 ) -> Result<Vec<(PathBuf, crate::resolver::Node<Self::ParsedSource>)>> {
389 let mut vyper = Sources::new();
390 sources.retain(|path, source| {
391 if let Ok(lang) = guess_lang(path) {
392 match lang {
393 MultiCompilerLanguage::Solc(_) => {}
394 MultiCompilerLanguage::Vyper(_) => {
395 vyper.insert(path.clone(), source.clone());
396 return false;
397 }
398 }
399 }
400 true
401 });
402
403 let solc_nodes = self.solc.parse_sources(sources)?;
404 let vyper_nodes = self.vyper.parse_sources(&mut vyper)?;
405 Ok(solc_nodes
406 .into_iter()
407 .map(|(k, v)| (k, v.map_data(MultiCompilerParsedSource::Solc)))
408 .chain(
409 vyper_nodes
410 .into_iter()
411 .map(|(k, v)| (k, v.map_data(MultiCompilerParsedSource::Vyper))),
412 )
413 .collect())
414 }
415}
416
417impl ParsedSource for MultiCompilerParsedSource {
418 type Language = MultiCompilerLanguage;
419
420 fn parse(content: &str, file: &Path) -> Result<Self> {
421 match guess_lang(file)? {
422 MultiCompilerLanguage::Solc(_) => {
423 <SolData as ParsedSource>::parse(content, file).map(Self::Solc)
424 }
425 MultiCompilerLanguage::Vyper(_) => {
426 VyperParsedSource::parse(content, file).map(Self::Vyper)
427 }
428 }
429 }
430
431 fn version_req(&self) -> Option<&semver::VersionReq> {
432 match self {
433 Self::Solc(parsed) => parsed.version_req(),
434 Self::Vyper(parsed) => parsed.version_req(),
435 }
436 }
437
438 fn contract_names(&self) -> &[String] {
439 match self {
440 Self::Solc(parsed) => parsed.contract_names(),
441 Self::Vyper(parsed) => parsed.contract_names(),
442 }
443 }
444
445 fn language(&self) -> Self::Language {
446 match self {
447 Self::Solc(parsed) => MultiCompilerLanguage::Solc(parsed.language()),
448 Self::Vyper(parsed) => MultiCompilerLanguage::Vyper(parsed.language()),
449 }
450 }
451
452 fn resolve_imports<C>(
453 &self,
454 paths: &crate::ProjectPathsConfig<C>,
455 include_paths: &mut BTreeSet<PathBuf>,
456 ) -> Result<Vec<PathBuf>> {
457 match self {
458 Self::Solc(parsed) => parsed.resolve_imports(paths, include_paths),
459 Self::Vyper(parsed) => parsed.resolve_imports(paths, include_paths),
460 }
461 }
462
463 fn compilation_dependencies<'a>(
464 &self,
465 imported_nodes: impl Iterator<Item = (&'a Path, &'a Self)>,
466 ) -> impl Iterator<Item = &'a Path>
467 where
468 Self: 'a,
469 {
470 match self {
471 Self::Solc(parsed) => parsed
472 .compilation_dependencies(
473 imported_nodes.filter_map(|(path, node)| node.solc().map(|node| (path, node))),
474 )
475 .collect::<Vec<_>>(),
476 Self::Vyper(parsed) => parsed
477 .compilation_dependencies(
478 imported_nodes.filter_map(|(path, node)| node.vyper().map(|node| (path, node))),
479 )
480 .collect::<Vec<_>>(),
481 }
482 .into_iter()
483 }
484}
485
486fn guess_lang(path: &Path) -> Result<MultiCompilerLanguage> {
487 let extension = path
488 .extension()
489 .and_then(|e| e.to_str())
490 .ok_or_else(|| SolcError::msg("failed to resolve file extension"))?;
491 if SOLC_EXTENSIONS.contains(&extension) {
492 Ok(MultiCompilerLanguage::Solc(match extension {
493 "sol" => SolcLanguage::Solidity,
494 "yul" => SolcLanguage::Yul,
495 _ => unreachable!(),
496 }))
497 } else if VYPER_EXTENSIONS.contains(&extension) {
498 Ok(MultiCompilerLanguage::Vyper(VyperLanguage::default()))
499 } else {
500 Err(SolcError::msg("unexpected file extension"))
501 }
502}
503
504impl CompilationError for MultiCompilerError {
505 fn is_warning(&self) -> bool {
506 match self {
507 Self::Solc(error) => error.is_warning(),
508 Self::Vyper(error) => error.is_warning(),
509 }
510 }
511 fn is_error(&self) -> bool {
512 match self {
513 Self::Solc(error) => error.is_error(),
514 Self::Vyper(error) => error.is_error(),
515 }
516 }
517
518 fn source_location(&self) -> Option<SourceLocation> {
519 match self {
520 Self::Solc(error) => error.source_location(),
521 Self::Vyper(error) => error.source_location(),
522 }
523 }
524
525 fn severity(&self) -> Severity {
526 match self {
527 Self::Solc(error) => error.severity(),
528 Self::Vyper(error) => error.severity(),
529 }
530 }
531
532 fn error_code(&self) -> Option<u64> {
533 match self {
534 Self::Solc(error) => error.error_code(),
535 Self::Vyper(error) => error.error_code(),
536 }
537 }
538}