Separate compilation and linker phases (#376)

Separate between compilation and linker phases to allow deploy time
linking and back-porting era compiler changes to fix #91. Unlinked
contract binaries (caused by missing libraries or missing factory
dependencies in turn) are emitted as raw ELF object.

Few drive by fixes:
- #98
- A compiler panic on missing libraries definitions.
- Fixes some incosistent type forwarding in JSON output (empty string
vs. null object).
- Remove the unused fallback for size optimization setting.
- Remove the broken `--lvm-ir`  mode.
- CI workflow fixes.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2025-09-27 20:52:22 +02:00
committed by GitHub
parent 13faedf08a
commit 94ec34c4d5
169 changed files with 6288 additions and 5206 deletions
@@ -1,14 +1,19 @@
//! The `solc --standard-json` output error.
pub mod source_location;
use std::str::FromStr;
use std::collections::BTreeMap;
use serde::Deserialize;
use serde::Serialize;
use crate::SolcStandardJsonInputSource;
use self::mapped_location::MappedLocation;
use self::source_location::SourceLocation;
pub mod error_handler;
pub mod mapped_location;
pub mod source_location;
/// The `solc --standard-json` output error.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
@@ -30,29 +35,81 @@ pub struct Error {
}
impl Error {
/// Returns the `ecrecover` function usage warning.
pub fn message_ecrecover(src: Option<&str>) -> Self {
let message = r#"
Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.
Polkadot comes with native account abstraction support, therefore it is highly recommended NOT
to rely on the fact that the account has an ECDSA private key attached to it since accounts might
implement other signature schemes.
"#
.to_owned();
/// The list of ignored `solc` warnings that are strictly EVM-related.
pub const IGNORED_WARNING_CODES: [&'static str; 5] = ["1699", "3860", "5159", "5574", "6417"];
/// A shortcut constructor.
pub fn new<S>(
r#type: &str,
message: S,
source_location: Option<SourceLocation>,
sources: Option<&BTreeMap<String, SolcStandardJsonInputSource>>,
) -> Self
where
S: std::fmt::Display,
{
let message = message.to_string();
let message_trimmed = message.trim();
let mut formatted_message = if message_trimmed.starts_with(r#type) {
message_trimmed.to_owned()
} else {
format!("{type}: {message_trimmed}")
};
formatted_message.push('\n');
if let Some(ref source_location) = source_location {
let source_code = sources.and_then(|sources| {
sources
.get(source_location.file.as_str())
.and_then(|source| source.content())
});
let mapped_location =
MappedLocation::try_from_source_location(source_location, source_code);
formatted_message.push_str(mapped_location.to_string().as_str());
formatted_message.push('\n');
}
Self {
component: "general".to_owned(),
error_code: None,
formatted_message: message.clone(),
formatted_message,
message,
severity: "warning".to_owned(),
source_location: src.map(SourceLocation::from_str).and_then(Result::ok),
r#type: "Warning".to_owned(),
severity: r#type.to_lowercase(),
source_location,
r#type: r#type.to_owned(),
}
}
/// A shortcut constructor.
pub fn new_error<S>(
message: S,
source_location: Option<SourceLocation>,
sources: Option<&BTreeMap<String, SolcStandardJsonInputSource>>,
) -> Self
where
S: std::fmt::Display,
{
Self::new("Error", message, source_location, sources)
}
/// A shortcut constructor.
pub fn new_warning<S>(
message: S,
source_location: Option<SourceLocation>,
sources: Option<&BTreeMap<String, SolcStandardJsonInputSource>>,
) -> Self
where
S: std::fmt::Display,
{
Self::new("Warning", message, source_location, sources)
}
/// Returns the `<address payable>`'s `send` and `transfer` methods usage error.
pub fn message_send_and_transfer(src: Option<&str>) -> Self {
pub fn warning_send_and_transfer(
node: Option<&str>,
id_paths: &BTreeMap<usize, &String>,
sources: &BTreeMap<String, SolcStandardJsonInputSource>,
) -> Self {
let message = r#"
Warning: It looks like you are using '<address payable>.send/transfer(<X>)'.
Using '<address payable>.send/transfer(<X>)' is deprecated and strongly discouraged!
@@ -65,43 +122,19 @@ and https://docs.soliditylang.org/en/latest/common-patterns.html#withdrawal-from
"#
.to_owned();
Self {
component: "general".to_owned(),
error_code: None,
formatted_message: message.clone(),
Self::new_warning(
message,
severity: "warning".to_owned(),
source_location: src.map(SourceLocation::from_str).and_then(Result::ok),
r#type: "Warning".to_owned(),
}
}
/// Returns the `extcodesize` instruction usage warning.
pub fn message_extcodesize(src: Option<&str>) -> Self {
let message = r#"
Warning: Your code or one of its dependencies uses the 'extcodesize' instruction, which is
usually needed in the following cases:
1. To detect whether an address belongs to a smart contract.
2. To detect whether the deploy code execution has finished.
Polkadot comes with native account abstraction support (so smart contracts are just accounts
coverned by code), and you should avoid differentiating between contracts and non-contract
addresses.
"#
.to_owned();
Self {
component: "general".to_owned(),
error_code: None,
formatted_message: message.clone(),
message,
severity: "warning".to_owned(),
source_location: src.map(SourceLocation::from_str).and_then(Result::ok),
r#type: "Warning".to_owned(),
}
node.and_then(|node| SourceLocation::try_from_ast(node, id_paths)),
Some(sources),
)
}
/// Returns the `origin` instruction usage warning.
pub fn message_tx_origin(src: Option<&str>) -> Self {
pub fn warning_tx_origin(
node: Option<&str>,
id_paths: &BTreeMap<usize, &String>,
sources: &BTreeMap<String, SolcStandardJsonInputSource>,
) -> Self {
let message = r#"
Warning: You are checking for 'tx.origin' in your code, which might lead to unexpected behavior.
Polkadot comes with native account abstraction support, and therefore the initiator of a
@@ -110,15 +143,28 @@ to rely on tx.origin, but use msg.sender instead.
"#
.to_owned();
Self {
component: "general".to_owned(),
error_code: None,
formatted_message: message.clone(),
Self::new_warning(
message,
severity: "warning".to_owned(),
source_location: src.map(SourceLocation::from_str).and_then(Result::ok),
r#type: "Warning".to_owned(),
}
node.and_then(|node| SourceLocation::try_from_ast(node, id_paths)),
Some(sources),
)
}
/// Returns the `runtimeCode` code usage error.
pub fn error_runtime_code(
node: Option<&str>,
id_paths: &BTreeMap<usize, &String>,
sources: &BTreeMap<String, SolcStandardJsonInputSource>,
) -> Self {
let message = r#"
Deploy and runtime code are merged in PVM, accessing `type(T).runtimeCode` is not possible.
Please consider changing the functionality relying on reading runtime code to a different approach.
"#;
Self::new_error(
message,
node.and_then(|node| SourceLocation::try_from_ast(node, id_paths)),
Some(sources),
)
}
/// Appends the contract path to the message..
@@ -126,6 +172,16 @@ to rely on tx.origin, but use msg.sender instead.
self.formatted_message
.push_str(format!("\n--> {path}\n").as_str());
}
/// Returns true if this is an error.
pub fn is_error(&self) -> bool {
self.severity == "error"
}
/// Returns true if this is a warning.
pub fn is_warning(&self) -> bool {
self.severity == "warning"
}
}
impl std::fmt::Display for Error {