foundry_common/preprocessor/
mod.rs
1use foundry_compilers::{
2 Compiler, Language, ProjectPathsConfig, apply_updates,
3 artifacts::SolcLanguage,
4 error::Result,
5 multi::{MultiCompiler, MultiCompilerInput, MultiCompilerLanguage},
6 project::Preprocessor,
7 solc::{SolcCompiler, SolcVersionedInput},
8};
9use solar_parse::{
10 ast::Span,
11 interface::{Session, SourceMap},
12};
13use solar_sema::{ParsingContext, thread_local::ThreadLocal};
14use std::{collections::HashSet, ops::Range, path::PathBuf};
15
16mod data;
17use data::{collect_preprocessor_data, create_deploy_helpers};
18
19mod deps;
20use deps::{PreprocessorDependencies, remove_bytecode_dependencies};
21
22#[track_caller]
24fn span_to_range(source_map: &SourceMap, span: Span) -> Range<usize> {
25 source_map.span_to_source(span).unwrap().1
26}
27
28#[derive(Debug)]
29pub struct TestOptimizerPreprocessor;
30
31impl Preprocessor<SolcCompiler> for TestOptimizerPreprocessor {
32 fn preprocess(
33 &self,
34 _solc: &SolcCompiler,
35 input: &mut SolcVersionedInput,
36 paths: &ProjectPathsConfig<SolcLanguage>,
37 mocks: &mut HashSet<PathBuf>,
38 ) -> Result<()> {
39 if !input.input.sources.iter().any(|(path, _)| paths.is_test_or_script(path)) {
41 trace!("no tests or sources to preprocess");
42 return Ok(());
43 }
44
45 let sess = solar_session_from_solc(input);
46 let _ = sess.enter_parallel(|| -> solar_parse::interface::Result {
47 let mut parsing_context = solar_pcx_from_solc_no_sources(&sess, input, paths);
49
50 let mut preprocessed_paths = vec![];
54 let sources = &mut input.input.sources;
55 for (path, source) in sources.iter() {
56 if let Ok(src_file) =
57 sess.source_map().new_source_file(path.clone(), source.content.as_str())
58 && paths.is_test_or_script(path)
59 {
60 parsing_context.add_file(src_file);
61 preprocessed_paths.push(path.clone());
62 }
63 }
64
65 let hir_arena = ThreadLocal::new();
67 if let Some(gcx) = parsing_context.parse_and_lower(&hir_arena)? {
68 let hir = &gcx.get().hir;
69 let deps = PreprocessorDependencies::new(
71 &sess,
72 hir,
73 &preprocessed_paths,
74 &paths.paths_relative().sources,
75 &paths.root,
76 mocks,
77 );
78 let data = collect_preprocessor_data(&sess, hir, &deps.referenced_contracts);
80
81 sources.extend(create_deploy_helpers(&data));
83
84 apply_updates(sources, remove_bytecode_dependencies(hir, &deps, &data));
86 }
87
88 Ok(())
89 });
90
91 if let Err(err) = sess.emitted_errors().unwrap() {
93 warn!("failed preprocessing {err}");
94 }
95
96 Ok(())
97 }
98}
99
100impl Preprocessor<MultiCompiler> for TestOptimizerPreprocessor {
101 fn preprocess(
102 &self,
103 compiler: &MultiCompiler,
104 input: &mut <MultiCompiler as Compiler>::Input,
105 paths: &ProjectPathsConfig<MultiCompilerLanguage>,
106 mocks: &mut HashSet<PathBuf>,
107 ) -> Result<()> {
108 let MultiCompilerInput::Solc(input) = input else { return Ok(()) };
110
111 let Some(solc) = &compiler.solc else { return Ok(()) };
112
113 let paths = paths.clone().with_language::<SolcLanguage>();
114 self.preprocess(solc, input, &paths, mocks)
115 }
116}
117
118fn solar_session_from_solc(solc: &SolcVersionedInput) -> Session {
119 use solar_parse::interface::config;
120
121 Session::builder()
122 .with_buffer_emitter(Default::default())
123 .opts(config::Opts {
124 language: match solc.input.language {
125 SolcLanguage::Solidity => config::Language::Solidity,
126 SolcLanguage::Yul => config::Language::Yul,
127 _ => unimplemented!(),
128 },
129
130 ..Default::default()
135 })
136 .build()
137}
138
139fn solar_pcx_from_solc_no_sources<'sess>(
140 sess: &'sess Session,
141 solc: &SolcVersionedInput,
142 paths: &ProjectPathsConfig<impl Language>,
143) -> ParsingContext<'sess> {
144 let mut pcx = ParsingContext::new(sess);
145 pcx.file_resolver.set_current_dir(solc.cli_settings.base_path.as_ref().unwrap_or(&paths.root));
146 for remapping in &paths.remappings {
147 pcx.file_resolver.add_import_remapping(solar_sema::interface::config::ImportRemapping {
148 context: remapping.context.clone().unwrap_or_default(),
149 prefix: remapping.name.clone(),
150 path: remapping.path.clone(),
151 });
152 }
153 pcx.file_resolver.add_include_paths(solc.cli_settings.include_paths.iter().cloned());
154 pcx
155}