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 From<SolData> for MultiCompilerParsedSource {
149 fn from(data: SolData) -> Self {
150 Self::Solc(data)
151 }
152}
153
154impl From<VyperParsedSource> for MultiCompilerParsedSource {
155 fn from(data: VyperParsedSource) -> Self {
156 Self::Vyper(data)
157 }
158}
159
160impl MultiCompilerParsedSource {
161 fn solc(&self) -> Option<&SolData> {
162 match self {
163 Self::Solc(parsed) => Some(parsed),
164 _ => None,
165 }
166 }
167
168 fn vyper(&self) -> Option<&VyperParsedSource> {
169 match self {
170 Self::Vyper(parsed) => Some(parsed),
171 _ => None,
172 }
173 }
174}
175
176#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
178#[serde(untagged)]
179pub enum MultiCompilerError {
180 Solc(Error),
181 Vyper(VyperCompilationError),
182}
183
184impl fmt::Display for MultiCompilerError {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 match self {
187 Self::Solc(error) => error.fmt(f),
188 Self::Vyper(error) => error.fmt(f),
189 }
190 }
191}
192
193#[derive(Clone, Copy, Debug, Default)]
194pub struct MultiCompilerRestrictions {
195 pub solc: SolcRestrictions,
196 pub vyper: VyperRestrictions,
197}
198
199impl CompilerSettingsRestrictions for MultiCompilerRestrictions {
200 fn merge(self, other: Self) -> Option<Self> {
201 Some(Self { solc: self.solc.merge(other.solc)?, vyper: self.vyper.merge(other.vyper)? })
202 }
203}
204
205#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
207pub struct MultiCompilerSettings {
208 pub solc: SolcSettings,
209 pub vyper: VyperSettings,
210}
211
212impl CompilerSettings for MultiCompilerSettings {
213 type Restrictions = MultiCompilerRestrictions;
214
215 fn can_use_cached(&self, other: &Self) -> bool {
216 self.solc.can_use_cached(&other.solc) && self.vyper.can_use_cached(&other.vyper)
217 }
218
219 fn update_output_selection(&mut self, mut f: impl FnMut(&mut OutputSelection)) {
220 self.solc.update_output_selection(&mut f);
221 self.vyper.update_output_selection(f);
222 }
223
224 fn with_allow_paths(self, allowed_paths: &BTreeSet<PathBuf>) -> Self {
225 Self {
226 solc: self.solc.with_allow_paths(allowed_paths),
227 vyper: self.vyper.with_allow_paths(allowed_paths),
228 }
229 }
230
231 fn with_base_path(self, base_path: &Path) -> Self {
232 Self {
233 solc: self.solc.with_base_path(base_path),
234 vyper: self.vyper.with_base_path(base_path),
235 }
236 }
237
238 fn with_include_paths(self, include_paths: &BTreeSet<PathBuf>) -> Self {
239 Self {
240 solc: self.solc.with_include_paths(include_paths),
241 vyper: self.vyper.with_include_paths(include_paths),
242 }
243 }
244
245 fn with_remappings(self, remappings: &[Remapping]) -> Self {
246 Self {
247 solc: self.solc.with_remappings(remappings),
248 vyper: self.vyper.with_remappings(remappings),
249 }
250 }
251
252 fn satisfies_restrictions(&self, restrictions: &Self::Restrictions) -> bool {
253 self.solc.satisfies_restrictions(&restrictions.solc)
254 && self.vyper.satisfies_restrictions(&restrictions.vyper)
255 }
256}
257
258impl From<MultiCompilerSettings> for SolcSettings {
259 fn from(settings: MultiCompilerSettings) -> Self {
260 settings.solc
261 }
262}
263
264impl From<MultiCompilerSettings> for VyperSettings {
265 fn from(settings: MultiCompilerSettings) -> Self {
266 settings.vyper
267 }
268}
269
270#[derive(Clone, Debug, Serialize)]
272#[serde(untagged)]
273pub enum MultiCompilerInput {
274 Solc(SolcVersionedInput),
275 Vyper(VyperVersionedInput),
276}
277
278impl CompilerInput for MultiCompilerInput {
279 type Language = MultiCompilerLanguage;
280 type Settings = MultiCompilerSettings;
281
282 fn build(
283 sources: Sources,
284 settings: Self::Settings,
285 language: Self::Language,
286 version: Version,
287 ) -> Self {
288 match language {
289 MultiCompilerLanguage::Solc(language) => {
290 Self::Solc(SolcVersionedInput::build(sources, settings.solc, language, version))
291 }
292 MultiCompilerLanguage::Vyper(language) => {
293 Self::Vyper(VyperVersionedInput::build(sources, settings.vyper, language, version))
294 }
295 }
296 }
297
298 fn compiler_name(&self) -> Cow<'static, str> {
299 match self {
300 Self::Solc(input) => input.compiler_name(),
301 Self::Vyper(input) => input.compiler_name(),
302 }
303 }
304
305 fn language(&self) -> Self::Language {
306 match self {
307 Self::Solc(input) => MultiCompilerLanguage::Solc(input.language()),
308 Self::Vyper(input) => MultiCompilerLanguage::Vyper(input.language()),
309 }
310 }
311
312 fn strip_prefix(&mut self, base: &Path) {
313 match self {
314 Self::Solc(input) => input.strip_prefix(base),
315 Self::Vyper(input) => input.strip_prefix(base),
316 }
317 }
318
319 fn version(&self) -> &Version {
320 match self {
321 Self::Solc(input) => input.version(),
322 Self::Vyper(input) => input.version(),
323 }
324 }
325
326 fn sources(&self) -> impl Iterator<Item = (&Path, &Source)> {
327 let ret: Box<dyn Iterator<Item = _>> = match self {
328 Self::Solc(input) => Box::new(input.sources()),
329 Self::Vyper(input) => Box::new(input.sources()),
330 };
331
332 ret
333 }
334}
335
336impl Compiler for MultiCompiler {
337 type Input = MultiCompilerInput;
338 type CompilationError = MultiCompilerError;
339 type Parser = MultiCompilerParser;
340 type Settings = MultiCompilerSettings;
341 type Language = MultiCompilerLanguage;
342 type CompilerContract = Contract;
343
344 fn compile(
345 &self,
346 input: &Self::Input,
347 ) -> Result<CompilerOutput<Self::CompilationError, Self::CompilerContract>> {
348 match input {
349 MultiCompilerInput::Solc(input) => {
350 if let Some(solc) = &self.solc {
351 Compiler::compile(solc, input).map(|res| res.map_err(MultiCompilerError::Solc))
352 } else {
353 Err(SolcError::msg("solc compiler is not available"))
354 }
355 }
356 MultiCompilerInput::Vyper(input) => {
357 if let Some(vyper) = &self.vyper {
358 Compiler::compile(vyper, input)
359 .map(|res| res.map_err(MultiCompilerError::Vyper))
360 } else {
361 Err(SolcError::msg("vyper compiler is not available"))
362 }
363 }
364 }
365 }
366
367 fn available_versions(&self, language: &Self::Language) -> Vec<CompilerVersion> {
368 match language {
369 MultiCompilerLanguage::Solc(language) => {
370 self.solc.as_ref().map(|s| s.available_versions(language)).unwrap_or_default()
371 }
372 MultiCompilerLanguage::Vyper(language) => {
373 self.vyper.as_ref().map(|v| v.available_versions(language)).unwrap_or_default()
374 }
375 }
376 }
377}
378
379impl SourceParser for MultiCompilerParser {
380 type ParsedSource = MultiCompilerParsedSource;
381
382 fn new(config: &crate::ProjectPathsConfig) -> Self {
383 Self { solc: SolParser::new(config), vyper: VyperParser::new(config) }
384 }
385
386 fn read(&mut self, path: &Path) -> Result<crate::resolver::Node<Self::ParsedSource>> {
387 Ok(match guess_lang(path)? {
388 MultiCompilerLanguage::Solc(_) => {
389 self.solc.read(path)?.map_data(MultiCompilerParsedSource::Solc)
390 }
391 MultiCompilerLanguage::Vyper(_) => {
392 self.vyper.read(path)?.map_data(MultiCompilerParsedSource::Vyper)
393 }
394 })
395 }
396
397 fn parse_sources(
398 &mut self,
399 sources: &mut Sources,
400 ) -> Result<Vec<(PathBuf, crate::resolver::Node<Self::ParsedSource>)>> {
401 let mut vyper = Sources::new();
402 sources.retain(|path, source| {
403 if let Ok(lang) = guess_lang(path) {
404 match lang {
405 MultiCompilerLanguage::Solc(_) => {}
406 MultiCompilerLanguage::Vyper(_) => {
407 vyper.insert(path.clone(), source.clone());
408 return false;
409 }
410 }
411 }
412 true
413 });
414
415 let solc_nodes = self.solc.parse_sources(sources)?;
416 let vyper_nodes = self.vyper.parse_sources(&mut vyper)?;
417 Ok(solc_nodes
418 .into_iter()
419 .map(|(k, v)| (k, v.map_data(MultiCompilerParsedSource::Solc)))
420 .chain(
421 vyper_nodes
422 .into_iter()
423 .map(|(k, v)| (k, v.map_data(MultiCompilerParsedSource::Vyper))),
424 )
425 .collect())
426 }
427
428 fn finalize_imports(
429 &mut self,
430 all_nodes: &mut Vec<crate::resolver::Node<Self::ParsedSource>>,
431 include_paths: &BTreeSet<PathBuf>,
432 ) -> Result<()> {
433 let mut solc_nodes = Vec::new();
435 let mut vyper_nodes = Vec::new();
436 let mut order = Vec::new();
437 for node in std::mem::take(all_nodes) {
438 order.push(node.data.language());
439 match node.data {
440 MultiCompilerParsedSource::Solc(_) => {
441 solc_nodes.push(node.map_data(|data| match data {
442 MultiCompilerParsedSource::Solc(data) => data,
443 _ => unreachable!(),
444 }));
445 }
446 MultiCompilerParsedSource::Vyper(_) => {
447 vyper_nodes.push(node.map_data(|data| match data {
448 MultiCompilerParsedSource::Vyper(data) => data,
449 _ => unreachable!(),
450 }));
451 }
452 }
453 }
454
455 self.solc.finalize_imports(&mut solc_nodes, include_paths)?;
456 self.vyper.finalize_imports(&mut vyper_nodes, include_paths)?;
457
458 let mut solc_nodes = solc_nodes.into_iter();
460 let mut vyper_nodes = vyper_nodes.into_iter();
461 for lang in order {
462 match lang {
463 MultiCompilerLanguage::Solc(_) => {
464 all_nodes.push(solc_nodes.next().unwrap().map_data(Into::into));
465 }
466 MultiCompilerLanguage::Vyper(_) => {
467 all_nodes.push(vyper_nodes.next().unwrap().map_data(Into::into));
468 }
469 }
470 }
471 assert!(solc_nodes.next().is_none());
472 assert!(vyper_nodes.next().is_none());
473
474 Ok(())
475 }
476}
477
478impl ParsedSource for MultiCompilerParsedSource {
479 type Language = MultiCompilerLanguage;
480
481 fn parse(content: &str, file: &Path) -> Result<Self> {
482 match guess_lang(file)? {
483 MultiCompilerLanguage::Solc(_) => {
484 <SolData as ParsedSource>::parse(content, file).map(Self::Solc)
485 }
486 MultiCompilerLanguage::Vyper(_) => {
487 VyperParsedSource::parse(content, file).map(Self::Vyper)
488 }
489 }
490 }
491
492 fn version_req(&self) -> Option<&semver::VersionReq> {
493 match self {
494 Self::Solc(parsed) => parsed.version_req(),
495 Self::Vyper(parsed) => parsed.version_req(),
496 }
497 }
498
499 fn contract_names(&self) -> &[String] {
500 match self {
501 Self::Solc(parsed) => parsed.contract_names(),
502 Self::Vyper(parsed) => parsed.contract_names(),
503 }
504 }
505
506 fn language(&self) -> Self::Language {
507 match self {
508 Self::Solc(parsed) => MultiCompilerLanguage::Solc(parsed.language()),
509 Self::Vyper(parsed) => MultiCompilerLanguage::Vyper(parsed.language()),
510 }
511 }
512
513 fn resolve_imports<C>(
514 &self,
515 paths: &crate::ProjectPathsConfig<C>,
516 include_paths: &mut BTreeSet<PathBuf>,
517 ) -> Result<Vec<PathBuf>> {
518 match self {
519 Self::Solc(parsed) => parsed.resolve_imports(paths, include_paths),
520 Self::Vyper(parsed) => parsed.resolve_imports(paths, include_paths),
521 }
522 }
523
524 fn compilation_dependencies<'a>(
525 &self,
526 imported_nodes: impl Iterator<Item = (&'a Path, &'a Self)>,
527 ) -> impl Iterator<Item = &'a Path>
528 where
529 Self: 'a,
530 {
531 match self {
532 Self::Solc(parsed) => parsed
533 .compilation_dependencies(
534 imported_nodes.filter_map(|(path, node)| node.solc().map(|node| (path, node))),
535 )
536 .collect::<Vec<_>>(),
537 Self::Vyper(parsed) => parsed
538 .compilation_dependencies(
539 imported_nodes.filter_map(|(path, node)| node.vyper().map(|node| (path, node))),
540 )
541 .collect::<Vec<_>>(),
542 }
543 .into_iter()
544 }
545}
546
547fn guess_lang(path: &Path) -> Result<MultiCompilerLanguage> {
548 let extension = path
549 .extension()
550 .and_then(|e| e.to_str())
551 .ok_or_else(|| SolcError::msg("failed to resolve file extension"))?;
552 if SOLC_EXTENSIONS.contains(&extension) {
553 Ok(MultiCompilerLanguage::Solc(match extension {
554 "sol" => SolcLanguage::Solidity,
555 "yul" => SolcLanguage::Yul,
556 _ => unreachable!(),
557 }))
558 } else if VYPER_EXTENSIONS.contains(&extension) {
559 Ok(MultiCompilerLanguage::Vyper(VyperLanguage::default()))
560 } else {
561 Err(SolcError::msg("unexpected file extension"))
562 }
563}
564
565impl CompilationError for MultiCompilerError {
566 fn is_warning(&self) -> bool {
567 match self {
568 Self::Solc(error) => error.is_warning(),
569 Self::Vyper(error) => error.is_warning(),
570 }
571 }
572 fn is_error(&self) -> bool {
573 match self {
574 Self::Solc(error) => error.is_error(),
575 Self::Vyper(error) => error.is_error(),
576 }
577 }
578
579 fn source_location(&self) -> Option<SourceLocation> {
580 match self {
581 Self::Solc(error) => error.source_location(),
582 Self::Vyper(error) => error.source_location(),
583 }
584 }
585
586 fn severity(&self) -> Severity {
587 match self {
588 Self::Solc(error) => error.severity(),
589 Self::Vyper(error) => error.severity(),
590 }
591 }
592
593 fn error_code(&self) -> Option<u64> {
594 match self {
595 Self::Solc(error) => error.error_code(),
596 Self::Vyper(error) => error.error_code(),
597 }
598 }
599}