Skip to content

Commit 55f23a7

Browse files
committed
Merge branch 'main' into matt/typos
2 parents 6d10b5e + 912fa49 commit 55f23a7

File tree

13 files changed

+185
-63
lines changed

13 files changed

+185
-63
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.13.1](https://github.com/foundry-rs/compilers/releases/tag/v0.13.1) - 2025-02-02
9+
10+
### Dependencies
11+
12+
- [deps] Bump dirs ([#243](https://github.com/foundry-rs/compilers/issues/243))
13+
14+
### Miscellaneous Tasks
15+
16+
- Clippy + winnow 0.7 ([#244](https://github.com/foundry-rs/compilers/issues/244))
17+
- Call shrink_to_fit afte parsing source maps ([#242](https://github.com/foundry-rs/compilers/issues/242))
18+
19+
## [0.13.0](https://github.com/foundry-rs/compilers/releases/tag/v0.13.0) - 2025-01-21
20+
21+
### Features
22+
23+
- Better artifact filenames for different profiles ([#241](https://github.com/foundry-rs/compilers/issues/241))
24+
- Add more features to reduce dependencies ([#239](https://github.com/foundry-rs/compilers/issues/239))
25+
26+
### Miscellaneous Tasks
27+
28+
- More lints ([#238](https://github.com/foundry-rs/compilers/issues/238))
29+
830
## [0.12.9](https://github.com/foundry-rs/compilers/releases/tag/v0.12.9) - 2025-01-05
931

1032
### Dependencies

Cargo.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ resolver = "2"
44

55
[workspace.package]
66
authors = ["Foundry Maintainers"]
7-
version = "0.12.9"
7+
version = "0.13.1"
88
rust-version = "1.83"
99
readme = "README.md"
1010
license = "MIT OR Apache-2.0"
@@ -35,11 +35,11 @@ redundant-lifetimes = "warn"
3535
all = "warn"
3636

3737
[workspace.dependencies]
38-
foundry-compilers = { path = "crates/compilers", version = "0.12.9" }
39-
foundry-compilers-artifacts = { path = "crates/artifacts/artifacts", version = "0.12.9" }
40-
foundry-compilers-artifacts-solc = { path = "crates/artifacts/solc", version = "0.12.9" }
41-
foundry-compilers-artifacts-vyper = { path = "crates/artifacts/vyper", version = "0.12.9" }
42-
foundry-compilers-core = { path = "crates/core", version = "0.12.9" }
38+
foundry-compilers = { path = "crates/compilers", version = "0.13.1" }
39+
foundry-compilers-artifacts = { path = "crates/artifacts/artifacts", version = "0.13.1" }
40+
foundry-compilers-artifacts-solc = { path = "crates/artifacts/solc", version = "0.13.1" }
41+
foundry-compilers-artifacts-vyper = { path = "crates/artifacts/vyper", version = "0.13.1" }
42+
foundry-compilers-core = { path = "crates/core", version = "0.13.1" }
4343

4444
alloy-json-abi = { version = "0.8", features = ["serde_json"] }
4545
alloy-primitives = { version = "0.8", features = ["serde", "rand"] }

crates/artifacts/solc/src/error.rs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
33
use std::{fmt, ops::Range, str::FromStr};
44
use yansi::{Color, Style};
55

6+
const ARROW: &str = "-->";
7+
68
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
79
pub struct SourceLocation {
810
pub file: String,
@@ -146,22 +148,30 @@ impl fmt::Display for Error {
146148

147149
let mut lines = fmtd_msg.lines();
148150

149-
// skip the first line if it contains the same message as the one we just formatted,
150-
// unless it also contains a source location, in which case the entire error message is an
151-
// old style error message, like:
152-
// path/to/file:line:column: ErrorType: message
153-
if lines
154-
.clone()
155-
.next()
156-
.is_some_and(|l| l.contains(short_msg) && l.bytes().filter(|b| *b == b':').count() < 3)
157-
{
158-
let _ = lines.next();
151+
if let Some(l) = lines.clone().next() {
152+
if l.bytes().filter(|&b| b == b':').count() >= 3
153+
&& (l.contains(['/', '\\']) || l.contains(".sol"))
154+
{
155+
// This is an old style error message, like:
156+
// path/to/file:line:column: ErrorType: message
157+
// We want to display this as-is.
158+
} else {
159+
// Otherwise, assume that the messages are the same until we find a source
160+
// location.
161+
lines.next();
162+
while let Some(line) = lines.clone().next() {
163+
if line.contains(ARROW) {
164+
break;
165+
}
166+
lines.next();
167+
}
168+
}
159169
}
160170

161-
// format the main source location
171+
// Format the main source location.
162172
fmt_source_location(f, &mut lines)?;
163173

164-
// format remaining lines as secondary locations
174+
// Format remaining lines as secondary locations.
165175
while let Some(line) = lines.next() {
166176
f.write_str("\n")?;
167177

@@ -260,11 +270,9 @@ fn fmt_source_location(f: &mut fmt::Formatter<'_>, lines: &mut std::str::Lines<'
260270
// --> source
261271
if let Some(line) = lines.next() {
262272
f.write_str("\n")?;
263-
264-
let arrow = "-->";
265-
if let Some((left, loc)) = line.split_once(arrow) {
273+
if let Some((left, loc)) = line.split_once(ARROW) {
266274
f.write_str(left)?;
267-
styled(f, Error::frame_style(), |f| f.write_str(arrow))?;
275+
styled(f, Error::frame_style(), |f| f.write_str(ARROW))?;
268276
f.write_str(loc)?;
269277
} else {
270278
f.write_str(line)?;
@@ -404,7 +412,7 @@ mod tests {
404412
}
405413

406414
#[test]
407-
fn solc_not_formatting_the_message1() {
415+
fn no_source_location() {
408416
let error = r#"{"component":"general","errorCode":"6553","formattedMessage":"SyntaxError: The msize instruction cannot be used when the Yul optimizer is activated because it can change its semantics. Either disable the Yul optimizer or do not use the instruction.\n\n","message":"The msize instruction cannot be used when the Yul optimizer is activated because it can change its semantics. Either disable the Yul optimizer or do not use the instruction.","severity":"error","sourceLocation":{"end":173,"file":"","start":114},"type":"SyntaxError"}"#;
409417
let error = serde_json::from_str::<Error>(error).unwrap();
410418
let s = error.to_string();
@@ -414,12 +422,31 @@ mod tests {
414422
}
415423

416424
#[test]
417-
fn solc_not_formatting_the_message2() {
425+
fn no_source_location2() {
418426
let error = r#"{"component":"general","errorCode":"5667","formattedMessage":"Warning: Unused function parameter. Remove or comment out the variable name to silence this warning.\n\n","message":"Unused function parameter. Remove or comment out the variable name to silence this warning.","severity":"warning","sourceLocation":{"end":104,"file":"","start":95},"type":"Warning"}"#;
419427
let error = serde_json::from_str::<Error>(error).unwrap();
420428
let s = error.to_string();
421429
eprintln!("{s}");
422430
assert!(s.contains("Warning (5667)"), "\n{s}");
423431
assert!(s.contains("Unused function parameter. Remove or comment out the variable name to silence this warning."), "\n{s}");
424432
}
433+
434+
#[test]
435+
fn stack_too_deep_multiline() {
436+
let error = r#"{"sourceLocation":{"file":"test/LibMap.t.sol","start":15084,"end":15113},"type":"YulException","component":"general","severity":"error","errorCode":null,"message":"Yul exception:Cannot swap Variable _23 with Slot RET[fun_assertEq]: too deep in the stack by 1 slots in [ var_136614_mpos RET _23 _21 _23 var_map_136608_slot _34 _34 _29 _33 _33 _39 expr_48 var_bitWidth var_map_136608_slot _26 _29 var_bitWidth TMP[eq, 0] RET[fun_assertEq] ]\nmemoryguard was present.","formattedMessage":"YulException: Cannot swap Variable _23 with Slot RET[fun_assertEq]: too deep in the stack by 1 slots in [ var_136614_mpos RET _23 _21 _23 var_map_136608_slot _34 _34 _29 _33 _33 _39 expr_48 var_bitWidth var_map_136608_slot _26 _29 var_bitWidth TMP[eq, 0] RET[fun_assertEq] ]\nmemoryguard was present.\n --> test/LibMap.t.sol:461:34:\n |\n461 | uint256 end = t.o - (t.o > 0 ? _random() % t.o : 0);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n"}"#;
437+
let error = serde_json::from_str::<Error>(error).unwrap();
438+
let s = error.to_string();
439+
eprintln!("{s}");
440+
assert_eq!(s.match_indices("Cannot swap Variable _23").count(), 1, "\n{s}");
441+
assert!(s.contains("-->"), "\n{s}");
442+
}
443+
444+
#[test]
445+
fn stack_too_deep_no_source_location() {
446+
let error = r#"{"type":"CompilerError","component":"general","severity":"error","errorCode":null,"message":"Compiler error (/solidity/libyul/backends/evm/AsmCodeGen.cpp:63):Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables. When compiling inline assembly: Variable key_ is 2 slot(s) too deep inside the stack. Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables.","formattedMessage":"CompilerError: Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables. When compiling inline assembly: Variable key_ is 2 slot(s) too deep inside the stack. Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables.\n\n"}"#;
447+
let error = serde_json::from_str::<Error>(error).unwrap();
448+
let s = error.to_string();
449+
eprintln!("{s}");
450+
assert_eq!(s.match_indices("too deep inside the stack.").count(), 1, "\n{s}");
451+
}
425452
}

crates/artifacts/solc/src/sourcemap.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -556,20 +556,23 @@ enum State {
556556

557557
impl State {
558558
fn advance(&mut self, pos: usize) -> Result<(), SyntaxError> {
559-
match self {
560-
Self::Offset => *self = Self::Length,
561-
Self::Length => *self = Self::Index,
562-
Self::Index => *self = Self::Jmp,
563-
Self::Jmp => *self = Self::Modifier,
559+
*self = match self {
560+
Self::Offset => Self::Length,
561+
Self::Length => Self::Index,
562+
Self::Index => Self::Jmp,
563+
Self::Jmp => Self::Modifier,
564564
Self::Modifier => return Err(SyntaxError::new(pos, "unexpected colon")),
565-
}
565+
};
566566
Ok(())
567567
}
568568
}
569569

570-
/// Parses a source map
570+
/// Parses a source map.
571571
pub fn parse(input: &str) -> Result<SourceMap, SyntaxError> {
572-
Parser::new(input).collect()
572+
Parser::new(input).collect::<Result<SourceMap, SyntaxError>>().map(|mut v| {
573+
v.shrink_to_fit();
574+
v
575+
})
573576
}
574577

575578
#[cfg(test)]

crates/compilers/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ futures-util = { workspace = true, optional = true }
3737
tokio = { workspace = true, optional = true }
3838

3939
auto_impl = "1"
40-
winnow = "0.6"
40+
winnow = "0.7"
4141
dyn-clone = "1"
4242
derive_more = { version = "1", features = ["debug"] }
4343
home = "0.5"
44-
dirs = "5.0"
44+
dirs = "6.0"
4545
itertools = ">=0.13, <=0.14"
4646

4747
# project-util

crates/compilers/src/artifact_output/mod.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use semver::Version;
1717
use serde::{de::DeserializeOwned, Deserialize, Serialize};
1818
use std::{
1919
borrow::Cow,
20-
collections::{btree_map::BTreeMap, HashSet},
20+
collections::{btree_map::BTreeMap, HashMap, HashSet},
2121
ffi::OsString,
2222
fmt, fs,
2323
hash::Hash,
@@ -621,8 +621,10 @@ pub trait ArtifactOutput {
621621
sources: &VersionedSourceFiles,
622622
layout: &ProjectPathsConfig<L>,
623623
ctx: OutputContext<'_>,
624+
primary_profiles: &HashMap<PathBuf, &str>,
624625
) -> Result<Artifacts<Self::Artifact>> {
625-
let mut artifacts = self.output_to_artifacts(contracts, sources, ctx, layout);
626+
let mut artifacts =
627+
self.output_to_artifacts(contracts, sources, ctx, layout, primary_profiles);
626628
fs::create_dir_all(&layout.artifacts).map_err(|err| {
627629
error!(dir=?layout.artifacts, "Failed to create artifacts folder");
628630
SolcIoError::new(err, &layout.artifacts)
@@ -850,6 +852,7 @@ pub trait ArtifactOutput {
850852
sources: &VersionedSourceFiles,
851853
ctx: OutputContext<'_>,
852854
layout: &ProjectPathsConfig<C>,
855+
primary_profiles: &HashMap<PathBuf, &str>,
853856
) -> Artifacts<Self::Artifact> {
854857
let mut artifacts = ArtifactsMap::new();
855858

@@ -877,6 +880,8 @@ pub trait ArtifactOutput {
877880
versioned_contracts.iter().map(|c| &c.version).collect::<HashSet<_>>();
878881
let unique_profiles =
879882
versioned_contracts.iter().map(|c| &c.profile).collect::<HashSet<_>>();
883+
let primary_profile = primary_profiles.get(file);
884+
880885
for contract in versioned_contracts {
881886
non_standalone_sources.insert(file);
882887

@@ -892,7 +897,8 @@ pub trait ArtifactOutput {
892897
&contract.version,
893898
&contract.profile,
894899
unique_versions.len() > 1,
895-
unique_profiles.len() > 1,
900+
unique_profiles.len() > 1
901+
&& primary_profile.is_none_or(|p| p != &contract.profile),
896902
);
897903

898904
taken_paths_lowercase.insert(artifact_path.to_slash_lossy().to_lowercase());
@@ -1122,8 +1128,15 @@ impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
11221128
sources: &VersionedSourceFiles,
11231129
layout: &ProjectPathsConfig<C>,
11241130
ctx: OutputContext<'_>,
1131+
primary_profiles: &HashMap<PathBuf, &str>,
11251132
) -> Result<Artifacts<Self::Artifact>> {
1126-
MinimalCombinedArtifacts::default().on_output(output, sources, layout, ctx)
1133+
MinimalCombinedArtifacts::default().on_output(
1134+
output,
1135+
sources,
1136+
layout,
1137+
ctx,
1138+
primary_profiles,
1139+
)
11271140
}
11281141

11291142
fn read_cached_artifact(path: &Path) -> Result<Self::Artifact> {

crates/compilers/src/compile/project.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ use crate::{
108108
filter::SparseOutputFilter,
109109
output::{AggregatedCompilerOutput, Builds},
110110
report,
111-
resolver::GraphEdges,
111+
resolver::{GraphEdges, ResolvedSources},
112112
ArtifactOutput, CompilerSettings, Graph, Project, ProjectCompileOutput, Sources,
113113
};
114114
use foundry_compilers_core::error::Result;
@@ -128,6 +128,8 @@ pub struct ProjectCompiler<
128128
/// Contains the relationship of the source files and their imports
129129
edges: GraphEdges<C::ParsedSource>,
130130
project: &'a Project<C, T>,
131+
/// A mapping from a source file path to the primary profile name selected for it.
132+
primary_profiles: HashMap<PathBuf, &'a str>,
131133
/// how to compile all the sources
132134
sources: CompilerSources<'a, C::Language, C::Settings>,
133135
}
@@ -152,7 +154,8 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
152154
sources.retain(|f, _| filter.is_match(f))
153155
}
154156
let graph = Graph::resolve_sources(&project.paths, sources)?;
155-
let (sources, edges) = graph.into_sources_by_version(project)?;
157+
let ResolvedSources { sources, primary_profiles, edges } =
158+
graph.into_sources_by_version(project)?;
156159

157160
// If there are multiple different versions, and we can use multiple jobs we can compile
158161
// them in parallel.
@@ -162,7 +165,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
162165
sources,
163166
};
164167

165-
Ok(Self { edges, project, sources })
168+
Ok(Self { edges, primary_profiles, project, sources })
166169
}
167170

168171
/// Compiles all the sources of the `Project` in the appropriate mode
@@ -199,7 +202,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
199202
/// - check cache
200203
fn preprocess(self) -> Result<PreprocessedState<'a, T, C>> {
201204
trace!("preprocessing");
202-
let Self { edges, project, mut sources } = self;
205+
let Self { edges, project, mut sources, primary_profiles } = self;
203206

204207
// convert paths on windows to ensure consistency with the `CompilerOutput` `solc` emits,
205208
// which is unix style `/`
@@ -209,7 +212,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
209212
// retain and compile only dirty sources and all their imports
210213
sources.filter(&mut cache);
211214

212-
Ok(PreprocessedState { sources, cache })
215+
Ok(PreprocessedState { sources, cache, primary_profiles })
213216
}
214217
}
215218

@@ -224,6 +227,9 @@ struct PreprocessedState<'a, T: ArtifactOutput<CompilerContract = C::CompilerCon
224227

225228
/// Cache that holds `CacheEntry` objects if caching is enabled and the project is recompiled
226229
cache: ArtifactsCache<'a, T, C>,
230+
231+
/// A mapping from a source file path to the primary profile name selected for it.
232+
primary_profiles: HashMap<PathBuf, &'a str>,
227233
}
228234

229235
impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
@@ -232,7 +238,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
232238
/// advance to the next state by compiling all sources
233239
fn compile(self) -> Result<CompiledState<'a, T, C>> {
234240
trace!("compiling");
235-
let PreprocessedState { sources, mut cache } = self;
241+
let PreprocessedState { sources, mut cache, primary_profiles } = self;
236242

237243
let mut output = sources.compile(&mut cache)?;
238244

@@ -243,7 +249,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
243249
// contracts again
244250
output.join_all(cache.project().root());
245251

246-
Ok(CompiledState { output, cache })
252+
Ok(CompiledState { output, cache, primary_profiles })
247253
}
248254
}
249255

@@ -252,6 +258,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
252258
struct CompiledState<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler> {
253259
output: AggregatedCompilerOutput<C>,
254260
cache: ArtifactsCache<'a, T, C>,
261+
primary_profiles: HashMap<PathBuf, &'a str>,
255262
}
256263

257264
impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
@@ -263,7 +270,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
263270
/// successful
264271
#[instrument(skip_all, name = "write-artifacts")]
265272
fn write_artifacts(self) -> Result<ArtifactsState<'a, T, C>> {
266-
let CompiledState { output, cache } = self;
273+
let CompiledState { output, cache, primary_profiles } = self;
267274

268275
let project = cache.project();
269276
let ctx = cache.output_ctx();
@@ -275,6 +282,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
275282
&output.sources,
276283
ctx,
277284
&project.paths,
285+
&primary_profiles,
278286
)
279287
} else if output.has_error(
280288
&project.ignored_error_codes,
@@ -287,6 +295,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
287295
&output.sources,
288296
ctx,
289297
&project.paths,
298+
&primary_profiles,
290299
)
291300
} else {
292301
trace!(
@@ -300,6 +309,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
300309
&output.sources,
301310
&project.paths,
302311
ctx,
312+
&primary_profiles,
303313
)?;
304314

305315
// emits all the build infos, if they exist

0 commit comments

Comments
 (0)