Skip to content

Commit 39ae553

Browse files
grandizzyDaniPopes
andauthored
fix(chisel & doc): backport v1.4 rc3 fixes (#11991)
* fix(doc): reuse solar sema compiler (#11980) * fix(doc): reuse solar sema compiler * fix: include only fn sig in docs * proper format docs, remove extra indent & append ; at enf of fn * changes after review: use get_ast_source, strip prefix always * fix(chisel): disable compiler optimizations (#11990) Add --ir-minimum like in `forge coverage`, and disable optimizations since we rely on source maps to inspect expressions. --------- Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
1 parent 0fa4aef commit 39ae553

File tree

8 files changed

+205
-156
lines changed

8 files changed

+205
-156
lines changed

crates/chisel/src/args.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ pub async fn run_command(args: Chisel) -> Result<()> {
5151
evm_opts,
5252
backend: None,
5353
calldata: None,
54+
ir_minimum: args.ir_minimum,
5455
})?;
5556

5657
// Execute prelude Solidity source files

crates/chisel/src/opts.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ pub struct Chisel {
3131
))]
3232
pub no_vm: bool,
3333

34+
/// Enable viaIR with minimum optimization
35+
///
36+
/// This can fix most of the "stack too deep" errors while resulting a
37+
/// relatively accurate source map.
38+
#[arg(long, help_heading = "REPL options")]
39+
pub ir_minimum: bool,
40+
3441
#[command(flatten)]
3542
pub build: BuildOpts,
3643

crates/chisel/src/source.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,11 @@ pub struct SessionSourceConfig {
382382
pub traces: bool,
383383
/// Optionally set calldata for the REPL contract execution
384384
pub calldata: Option<Vec<u8>>,
385+
/// Enable viaIR with minimum optimization
386+
///
387+
/// This can fix most of the "stack too deep" errors while resulting a
388+
/// relatively accurate source map.
389+
pub ir_minimum: bool,
385390
}
386391

387392
impl SessionSourceConfig {
@@ -603,7 +608,8 @@ impl SessionSource {
603608
fn compile(&self) -> Result<ProjectCompileOutput> {
604609
let sources = self.get_sources();
605610

606-
let project = self.config.foundry_config.ephemeral_project()?;
611+
let mut project = self.config.foundry_config.ephemeral_project()?;
612+
self.config.foundry_config.disable_optimizations(&mut project, self.config.ir_minimum);
607613
let mut output = ProjectCompiler::with_sources(&project, sources)?.compile()?;
608614

609615
if output.has_compiler_errors() {

crates/config/src/lib.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use foundry_compilers::{
3737
},
3838
error::SolcError,
3939
multi::{MultiCompilerParser, MultiCompilerRestrictions},
40-
solc::{CliSettings, SolcSettings},
40+
solc::{CliSettings, SolcLanguage, SolcSettings},
4141
};
4242
use regex::Regex;
4343
use revm::primitives::hardfork::SpecId;
@@ -1177,6 +1177,28 @@ impl Config {
11771177
Ok(project)
11781178
}
11791179

1180+
/// Disables optimizations and enables viaIR with minimum optimization if `ir_minimum` is true.
1181+
pub fn disable_optimizations(&self, project: &mut Project, ir_minimum: bool) {
1182+
if ir_minimum {
1183+
// Enable viaIR with minimum optimization: https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350
1184+
// And also in new releases of Solidity: https://github.com/ethereum/solidity/issues/13972#issuecomment-1628632202
1185+
project.settings.solc.settings = std::mem::take(&mut project.settings.solc.settings)
1186+
.with_via_ir_minimum_optimization();
1187+
1188+
// Sanitize settings for solc 0.8.4 if version cannot be detected: https://github.com/foundry-rs/foundry/issues/9322
1189+
// But keep the EVM version: https://github.com/ethereum/solidity/issues/15775
1190+
let evm_version = project.settings.solc.evm_version;
1191+
let version = self.solc_version().unwrap_or_else(|| Version::new(0, 8, 4));
1192+
project.settings.solc.settings.sanitize(&version, SolcLanguage::Solidity);
1193+
project.settings.solc.evm_version = evm_version;
1194+
} else {
1195+
project.settings.solc.optimizer.disable();
1196+
project.settings.solc.optimizer.runs = None;
1197+
project.settings.solc.optimizer.details = None;
1198+
project.settings.solc.via_ir = None;
1199+
}
1200+
}
1201+
11801202
/// Cleans the project.
11811203
pub fn cleanup<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>>(
11821204
&self,

crates/doc/src/builder.rs

Lines changed: 147 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ impl DocBuilder {
9494
}
9595

9696
/// Parse the sources and build the documentation.
97-
pub fn build(self) -> eyre::Result<()> {
97+
pub fn build(self, compiler: &mut solar::sema::Compiler) -> eyre::Result<()> {
9898
fs::create_dir_all(self.root.join(&self.config.out))
9999
.wrap_err("failed to create output directory")?;
100100

@@ -124,135 +124,149 @@ impl DocBuilder {
124124
.collect::<Vec<_>>();
125125

126126
let out_dir = self.out_dir()?;
127-
let documents = combined_sources
128-
.par_iter()
129-
.enumerate()
130-
.map(|(i, (path, from_library))| {
131-
let path = *path;
132-
let from_library = *from_library;
133-
134-
// Read and parse source file
135-
let source = fs::read_to_string(path)?;
136-
let source =
137-
forge_fmt::format(&source, self.fmt.clone()).into_result().unwrap_or(source);
138-
139-
let (mut source_unit, comments) = match solang_parser::parse(&source, i) {
140-
Ok(res) => res,
141-
Err(err) => {
142-
if from_library {
143-
// Ignore failures for library files
144-
return Ok(Vec::new());
145-
} else {
146-
return Err(eyre::eyre!(
147-
"Failed to parse Solidity code for {}\nDebug info: {:?}",
148-
path.display(),
149-
err
150-
));
127+
let documents = compiler.enter_mut(|compiler| -> eyre::Result<Vec<Vec<Document>>> {
128+
let gcx = compiler.gcx();
129+
let documents = combined_sources
130+
.par_iter()
131+
.enumerate()
132+
.map(|(i, (path, from_library))| {
133+
let path = *path;
134+
let from_library = *from_library;
135+
let mut files = vec![];
136+
137+
// Read and parse source file
138+
if let Some((_, ast)) = gcx.get_ast_source(path)
139+
&& let Some(source) =
140+
forge_fmt::format_ast(gcx, ast, self.fmt.clone().into())
141+
{
142+
let (mut source_unit, comments) = match solang_parser::parse(&source, i) {
143+
Ok(res) => res,
144+
Err(err) => {
145+
if from_library {
146+
// Ignore failures for library files
147+
return Ok(files);
148+
} else {
149+
return Err(eyre::eyre!(
150+
"Failed to parse Solidity code for {}\nDebug info: {:?}",
151+
path.display(),
152+
err
153+
));
154+
}
155+
}
156+
};
157+
158+
// Visit the parse tree
159+
let mut doc = Parser::new(comments, source);
160+
source_unit
161+
.visit(&mut doc)
162+
.map_err(|err| eyre::eyre!("Failed to parse source: {err}"))?;
163+
164+
// Split the parsed items on top-level constants and rest.
165+
let (items, consts): (Vec<ParseItem>, Vec<ParseItem>) = doc
166+
.items()
167+
.into_iter()
168+
.partition(|item| !matches!(item.source, ParseSource::Variable(_)));
169+
170+
// Attempt to group overloaded top-level functions
171+
let mut remaining = Vec::with_capacity(items.len());
172+
let mut funcs: HashMap<String, Vec<ParseItem>> = HashMap::default();
173+
for item in items {
174+
if matches!(item.source, ParseSource::Function(_)) {
175+
funcs.entry(item.source.ident()).or_default().push(item);
176+
} else {
177+
// Put the item back
178+
remaining.push(item);
179+
}
151180
}
152-
}
153-
};
154-
155-
// Visit the parse tree
156-
let mut doc = Parser::new(comments, source);
157-
source_unit
158-
.visit(&mut doc)
159-
.map_err(|err| eyre::eyre!("Failed to parse source: {err}"))?;
160-
161-
// Split the parsed items on top-level constants and rest.
162-
let (items, consts): (Vec<ParseItem>, Vec<ParseItem>) = doc
163-
.items()
164-
.into_iter()
165-
.partition(|item| !matches!(item.source, ParseSource::Variable(_)));
166-
167-
// Attempt to group overloaded top-level functions
168-
let mut remaining = Vec::with_capacity(items.len());
169-
let mut funcs: HashMap<String, Vec<ParseItem>> = HashMap::default();
170-
for item in items {
171-
if matches!(item.source, ParseSource::Function(_)) {
172-
funcs.entry(item.source.ident()).or_default().push(item);
173-
} else {
174-
// Put the item back
175-
remaining.push(item);
176-
}
177-
}
178-
let (items, overloaded): (
179-
HashMap<String, Vec<ParseItem>>,
180-
HashMap<String, Vec<ParseItem>>,
181-
) = funcs.into_iter().partition(|(_, v)| v.len() == 1);
182-
remaining.extend(items.into_iter().flat_map(|(_, v)| v));
183-
184-
// Each regular item will be written into its own file.
185-
let mut files = remaining
186-
.into_iter()
187-
.map(|item| {
188-
let relative_path = path.strip_prefix(&self.root)?.join(item.filename());
189-
190-
let target_path = out_dir.join(Self::SRC).join(relative_path);
191-
let ident = item.source.ident();
192-
Ok(Document::new(
193-
path.clone(),
194-
target_path,
195-
from_library,
196-
self.config.out.clone(),
197-
)
198-
.with_content(DocumentContent::Single(item), ident))
199-
})
200-
.collect::<eyre::Result<Vec<_>>>()?;
201-
202-
// If top-level constants exist, they will be written to the same file.
203-
if !consts.is_empty() {
204-
let filestem = path.file_stem().and_then(|stem| stem.to_str());
205-
206-
let filename = {
207-
let mut name = "constants".to_owned();
208-
if let Some(stem) = filestem {
209-
name.push_str(&format!(".{stem}"));
181+
let (items, overloaded): (
182+
HashMap<String, Vec<ParseItem>>,
183+
HashMap<String, Vec<ParseItem>>,
184+
) = funcs.into_iter().partition(|(_, v)| v.len() == 1);
185+
remaining.extend(items.into_iter().flat_map(|(_, v)| v));
186+
187+
// Each regular item will be written into its own file.
188+
files = remaining
189+
.into_iter()
190+
.map(|item| {
191+
let relative_path =
192+
path.strip_prefix(&self.root)?.join(item.filename());
193+
194+
let target_path = out_dir.join(Self::SRC).join(relative_path);
195+
let ident = item.source.ident();
196+
Ok(Document::new(
197+
path.clone(),
198+
target_path,
199+
from_library,
200+
self.config.out.clone(),
201+
)
202+
.with_content(DocumentContent::Single(item), ident))
203+
})
204+
.collect::<eyre::Result<Vec<_>>>()?;
205+
206+
// If top-level constants exist, they will be written to the same file.
207+
if !consts.is_empty() {
208+
let filestem = path.file_stem().and_then(|stem| stem.to_str());
209+
210+
let filename = {
211+
let mut name = "constants".to_owned();
212+
if let Some(stem) = filestem {
213+
name.push_str(&format!(".{stem}"));
214+
}
215+
name.push_str(".md");
216+
name
217+
};
218+
let relative_path = path.strip_prefix(&self.root)?.join(filename);
219+
let target_path = out_dir.join(Self::SRC).join(relative_path);
220+
221+
let identity = match filestem {
222+
Some(stem) if stem.to_lowercase().contains("constants") => {
223+
stem.to_owned()
224+
}
225+
Some(stem) => format!("{stem} constants"),
226+
None => "constants".to_owned(),
227+
};
228+
229+
files.push(
230+
Document::new(
231+
path.clone(),
232+
target_path,
233+
from_library,
234+
self.config.out.clone(),
235+
)
236+
.with_content(DocumentContent::Constants(consts), identity),
237+
)
210238
}
211-
name.push_str(".md");
212-
name
213-
};
214-
let relative_path = path.strip_prefix(&self.root)?.join(filename);
215-
let target_path = out_dir.join(Self::SRC).join(relative_path);
216239

217-
let identity = match filestem {
218-
Some(stem) if stem.to_lowercase().contains("constants") => stem.to_owned(),
219-
Some(stem) => format!("{stem} constants"),
220-
None => "constants".to_owned(),
240+
// If overloaded functions exist, they will be written to the same file
241+
if !overloaded.is_empty() {
242+
for (ident, funcs) in overloaded {
243+
let filename =
244+
funcs.first().expect("no overloaded functions").filename();
245+
let relative_path = path.strip_prefix(&self.root)?.join(filename);
246+
247+
let target_path = out_dir.join(Self::SRC).join(relative_path);
248+
files.push(
249+
Document::new(
250+
path.clone(),
251+
target_path,
252+
from_library,
253+
self.config.out.clone(),
254+
)
255+
.with_content(
256+
DocumentContent::OverloadedFunctions(funcs),
257+
ident,
258+
),
259+
);
260+
}
261+
}
221262
};
222263

223-
files.push(
224-
Document::new(
225-
path.clone(),
226-
target_path,
227-
from_library,
228-
self.config.out.clone(),
229-
)
230-
.with_content(DocumentContent::Constants(consts), identity),
231-
)
232-
}
264+
Ok(files)
265+
})
266+
.collect::<eyre::Result<Vec<_>>>()?;
233267

234-
// If overloaded functions exist, they will be written to the same file
235-
if !overloaded.is_empty() {
236-
for (ident, funcs) in overloaded {
237-
let filename = funcs.first().expect("no overloaded functions").filename();
238-
let relative_path = path.strip_prefix(&self.root)?.join(filename);
239-
240-
let target_path = out_dir.join(Self::SRC).join(relative_path);
241-
files.push(
242-
Document::new(
243-
path.clone(),
244-
target_path,
245-
from_library,
246-
self.config.out.clone(),
247-
)
248-
.with_content(DocumentContent::OverloadedFunctions(funcs), ident),
249-
);
250-
}
251-
}
252-
253-
Ok(files)
254-
})
255-
.collect::<eyre::Result<Vec<_>>>()?;
268+
Ok(documents)
269+
})?;
256270

257271
// Flatten results and apply preprocessors to files
258272
let documents = self
@@ -262,15 +276,17 @@ impl DocBuilder {
262276
p.preprocess(docs)
263277
})?;
264278

265-
// Sort the results
266-
let documents = documents.into_iter().sorted_by(|doc1, doc2| {
267-
doc1.item_path.display().to_string().cmp(&doc2.item_path.display().to_string())
268-
});
279+
// Sort the results and filter libraries.
280+
let documents = documents
281+
.into_iter()
282+
.sorted_by(|doc1, doc2| {
283+
doc1.item_path.display().to_string().cmp(&doc2.item_path.display().to_string())
284+
})
285+
.filter(|d| !d.from_library || self.include_libraries)
286+
.collect_vec();
269287

270288
// Write mdbook related files
271-
self.write_mdbook(
272-
documents.filter(|d| !d.from_library || self.include_libraries).collect_vec(),
273-
)?;
289+
self.write_mdbook(documents)?;
274290

275291
// Build the book if requested
276292
if self.should_build {

0 commit comments

Comments
 (0)