fix(solc): use lowercase when comparing paths (#1041)

* fix(solc): use lowercase when comparing paths

* trace keys

* test: add lowercase contract test
This commit is contained in:
Matthias Seitz 2022-03-17 09:27:03 +01:00 committed by GitHub
parent a2960a847d
commit 5314c4e618
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 7 deletions

View File

@ -741,12 +741,16 @@ impl CompilerOutput {
where where
I: IntoIterator<Item = &'a str>, I: IntoIterator<Item = &'a str>,
{ {
let files: HashSet<_> = files.into_iter().collect(); // Note: use `to_lowercase` here because solc not necessarily emits the exact file name,
// e.g. `src/utils/upgradeProxy.sol` is emitted as `src/utils/UpgradeProxy.sol`
self.contracts.retain(|f, _| files.contains(f.as_str())); let files: HashSet<_> = files.into_iter().map(|s| s.to_lowercase()).collect();
self.sources.retain(|f, _| files.contains(f.as_str())); self.contracts.retain(|f, _| files.contains(f.to_lowercase().as_str()));
self.sources.retain(|f, _| files.contains(f.to_lowercase().as_str()));
self.errors.retain(|err| { self.errors.retain(|err| {
err.source_location.as_ref().map(|s| files.contains(s.file.as_str())).unwrap_or(true) err.source_location
.as_ref()
.map(|s| files.contains(s.file.to_lowercase().as_str()))
.unwrap_or(true)
}); });
} }
@ -2050,8 +2054,38 @@ impl SourceFiles {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::AggregatedCompilerOutput;
use std::{fs, path::PathBuf}; use std::{fs, path::PathBuf};
#[test]
fn can_parse_declaration_error() {
let s = r#"{
"errors": [
{
"component": "general",
"errorCode": "7576",
"formattedMessage": "DeclarationError: Undeclared identifier. Did you mean \"revert\"?\n --> /Users/src/utils/UpgradeProxy.sol:35:17:\n |\n35 | refert(\"Transparent ERC1967 proxies do not have upgradeable implementations\");\n | ^^^^^^\n\n",
"message": "Undeclared identifier. Did you mean \"revert\"?",
"severity": "error",
"sourceLocation": {
"end": 1623,
"file": "/Users/src/utils/UpgradeProxy.sol",
"start": 1617
},
"type": "DeclarationError"
}
],
"sources": { }
}"#;
let out: CompilerOutput = serde_json::from_str(s).unwrap();
assert_eq!(out.errors.len(), 1);
let mut aggregated = AggregatedCompilerOutput::default();
aggregated.extend("0.8.12".parse().unwrap(), out);
assert!(!aggregated.is_unchanged());
}
#[test] #[test]
fn can_link_bytecode() { fn can_link_bytecode() {
// test cases taken from https://github.com/ethereum/solc-js/blob/master/test/linker.js // test cases taken from https://github.com/ethereum/solc-js/blob/master/test/linker.js

View File

@ -331,7 +331,7 @@ impl CompilerSources {
tracing::trace!( tracing::trace!(
"Detected {} dirty sources {:?}", "Detected {} dirty sources {:?}",
sources.dirty().count(), sources.dirty().count(),
sources.dirty_files() sources.dirty_files().collect::<Vec<_>>()
); );
(solc, (version, sources)) (solc, (version, sources))
}) })
@ -454,6 +454,7 @@ fn compile_sequential(
let output = solc.compile_exact(&input)?; let output = solc.compile_exact(&input)?;
report::solc_success(&solc, &version, &output); report::solc_success(&solc, &version, &output);
tracing::trace!("compiled input, output has error: {}", output.has_error()); tracing::trace!("compiled input, output has error: {}", output.has_error());
tracing::trace!("received compiler output: {:?}", output.contracts.keys());
aggregated.extend(version.clone(), output); aggregated.extend(version.clone(), output);
} }
} }

View File

@ -342,7 +342,7 @@ pub(crate) fn create_contract_file(path: PathBuf, content: impl AsRef<str>) -> R
} }
fn contract_file_name(name: impl AsRef<str>) -> String { fn contract_file_name(name: impl AsRef<str>) -> String {
let name = name.as_ref(); let name = name.as_ref().trim();
if name.ends_with(".sol") { if name.ends_with(".sol") {
name.to_string() name.to_string()
} else { } else {

View File

@ -684,6 +684,67 @@ fn can_recompile_with_changes() {
assert!(compiled.find("B").is_some()); assert!(compiled.find("B").is_some());
} }
#[test]
fn can_recompile_with_lowercase_names() {
init_tracing();
let tmp = TempProject::dapptools().unwrap();
tmp.add_source(
"deployProxy.sol",
r#"
pragma solidity =0.8.12;
contract DeployProxy {}
"#,
)
.unwrap();
let upgrade = r#"
pragma solidity =0.8.12;
import "./deployProxy.sol";
import "./ProxyAdmin.sol";
contract UpgradeProxy {}
"#;
tmp.add_source("upgradeProxy.sol", upgrade).unwrap();
tmp.add_source(
"ProxyAdmin.sol",
r#"
pragma solidity =0.8.12;
contract ProxyAdmin {}
"#,
)
.unwrap();
let compiled = tmp.compile().unwrap();
assert!(!compiled.has_compiler_errors());
assert!(compiled.find("DeployProxy").is_some());
assert!(compiled.find("UpgradeProxy").is_some());
assert!(compiled.find("ProxyAdmin").is_some());
let artifacts = tmp.artifacts_snapshot().unwrap();
assert_eq!(artifacts.artifacts.as_ref().len(), 3);
artifacts.assert_artifacts_essentials_present();
let compiled = tmp.compile().unwrap();
assert!(compiled.find("DeployProxy").is_some());
assert!(compiled.find("UpgradeProxy").is_some());
assert!(compiled.find("ProxyAdmin").is_some());
assert!(compiled.is_unchanged());
// modify upgradeProxy.sol
tmp.add_source("upgradeProxy.sol", format!("{}\n", upgrade)).unwrap();
let compiled = tmp.compile().unwrap();
assert!(!compiled.has_compiler_errors());
assert!(!compiled.is_unchanged());
assert!(compiled.find("DeployProxy").is_some());
assert!(compiled.find("UpgradeProxy").is_some());
assert!(compiled.find("ProxyAdmin").is_some());
let artifacts = tmp.artifacts_snapshot().unwrap();
assert_eq!(artifacts.artifacts.as_ref().len(), 3);
artifacts.assert_artifacts_essentials_present();
}
#[test] #[test]
fn can_recompile_unchanged_with_empty_files() { fn can_recompile_unchanged_with_empty_files() {
let tmp = TempProject::dapptools().unwrap(); let tmp = TempProject::dapptools().unwrap();