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