foundry_common/preprocessor/
mod.rs1use foundry_compilers::{
2 Compiler, Language, ProjectPathsConfig, apply_updates,
3 artifacts::SolcLanguage,
4 error::Result,
5 multi::{MultiCompiler, MultiCompilerInput, MultiCompilerLanguage, SolidityCompiler},
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) = (match &compiler.solidity {
112 SolidityCompiler::Solc(solc) => Some(solc),
113 _ => None,
114 }) else {
115 return Ok(());
116 };
117
118 let paths = paths.clone().with_language::<SolcLanguage>();
119 self.preprocess(solc, input, &paths, mocks)
120 }
121}
122
123fn solar_session_from_solc(solc: &SolcVersionedInput) -> Session {
124 use solar_parse::interface::config;
125
126 Session::builder()
127 .with_buffer_emitter(Default::default())
128 .opts(config::Opts {
129 language: match solc.input.language {
130 SolcLanguage::Solidity => config::Language::Solidity,
131 SolcLanguage::Yul => config::Language::Yul,
132 _ => unimplemented!(),
133 },
134
135 ..Default::default()
140 })
141 .build()
142}
143
144fn solar_pcx_from_solc_no_sources<'sess>(
145 sess: &'sess Session,
146 solc: &SolcVersionedInput,
147 paths: &ProjectPathsConfig<impl Language>,
148) -> ParsingContext<'sess> {
149 let mut pcx = ParsingContext::new(sess);
150 pcx.file_resolver.set_current_dir(solc.cli_settings.base_path.as_ref().unwrap_or(&paths.root));
151 for remapping in &paths.remappings {
152 pcx.file_resolver.add_import_remapping(solar_sema::interface::config::ImportRemapping {
153 context: remapping.context.clone().unwrap_or_default(),
154 prefix: remapping.name.clone(),
155 path: remapping.path.clone(),
156 });
157 }
158 pcx.file_resolver.add_include_paths(solc.cli_settings.include_paths.iter().cloned());
159 pcx
160}