foundry_common/preprocessor/
mod.rs1use crate::errors::convert_solar_errors;
2use foundry_compilers::{
3 Compiler, ProjectPathsConfig, SourceParser, apply_updates,
4 artifacts::SolcLanguage,
5 error::Result,
6 multi::{MultiCompiler, MultiCompilerInput, MultiCompilerLanguage},
7 project::Preprocessor,
8 solc::{SolcCompiler, SolcVersionedInput},
9};
10use solar::parse::{ast::Span, interface::SourceMap};
11use std::{
12 collections::HashSet,
13 ops::{ControlFlow, Range},
14 path::PathBuf,
15};
16
17mod data;
18use data::{collect_preprocessor_data, create_deploy_helpers};
19
20mod deps;
21use deps::{PreprocessorDependencies, remove_bytecode_dependencies};
22
23#[track_caller]
25fn span_to_range(source_map: &SourceMap, span: Span) -> Range<usize> {
26 source_map.span_to_range(span).unwrap()
27}
28
29#[derive(Debug)]
36pub struct DynamicTestLinkingPreprocessor;
37
38impl Preprocessor<SolcCompiler> for DynamicTestLinkingPreprocessor {
39 #[instrument(name = "DynamicTestLinkingPreprocessor::preprocess", skip_all)]
40 fn preprocess(
41 &self,
42 _solc: &SolcCompiler,
43 input: &mut SolcVersionedInput,
44 paths: &ProjectPathsConfig<SolcLanguage>,
45 mocks: &mut HashSet<PathBuf>,
46 ) -> Result<()> {
47 if !input.input.sources.iter().any(|(path, _)| paths.is_test_or_script(path)) {
49 trace!("no tests or scripts to preprocess");
50 return Ok(());
51 }
52
53 let mut compiler =
54 foundry_compilers::resolver::parse::SolParser::new(paths.with_language_ref())
55 .into_compiler();
56 let _ = compiler.enter_mut(|compiler| -> solar::interface::Result {
57 let mut pcx = compiler.parse();
58
59 let mut preprocessed_paths = vec![];
63 let mut script_paths = HashSet::new();
64 let sources = &mut input.input.sources;
65 for (path, source) in sources.iter() {
66 if let Ok(src_file) = compiler
67 .sess()
68 .source_map()
69 .new_source_file(path.clone(), source.content.as_str())
70 && paths.is_test_or_script(path)
71 {
72 pcx.add_file(src_file);
73 if paths.is_script(path) {
74 script_paths.insert(path.clone());
75 }
76 preprocessed_paths.push(path.clone());
77 }
78 }
79
80 pcx.parse();
82 let ControlFlow::Continue(()) = compiler.lower_asts()? else { return Ok(()) };
83 let gcx = compiler.gcx();
84 let deps = PreprocessorDependencies::new(
89 gcx,
90 &preprocessed_paths,
91 &script_paths,
92 &paths.paths_relative().sources,
93 &paths.root,
94 mocks,
95 );
96 let data = collect_preprocessor_data(gcx, &deps.referenced_contracts);
98
99 sources.extend(create_deploy_helpers(&data));
101
102 apply_updates(sources, remove_bytecode_dependencies(gcx, &deps, &data));
104
105 Ok(())
106 });
107
108 if let Err(err) = convert_solar_errors(compiler.dcx()) {
110 warn!(%err, "failed preprocessing");
111 }
112
113 Ok(())
114 }
115}
116
117impl Preprocessor<MultiCompiler> for DynamicTestLinkingPreprocessor {
118 fn preprocess(
119 &self,
120 compiler: &MultiCompiler,
121 input: &mut <MultiCompiler as Compiler>::Input,
122 paths: &ProjectPathsConfig<MultiCompilerLanguage>,
123 mocks: &mut HashSet<PathBuf>,
124 ) -> Result<()> {
125 let MultiCompilerInput::Solc(input) = input else { return Ok(()) };
127
128 let Some(solc) = &compiler.solc else { return Ok(()) };
129
130 let paths = paths.clone().with_language::<SolcLanguage>();
131 self.preprocess(solc, input, &paths, mocks)
132 }
133}