Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix error reporting for unmatched jump labels #19

Merged
merged 1 commit into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

## [Unreleased]

## [1.0.3] - 2025-01-12
- Remove huff-examples submodule
- Fix invalid error mapping for import wih unmatched jump label
- Print unmatched jump labels

## [1.0.2] - 2025-01-11
- Use latest stable Rust version 1.84
- Report error for invalid hex literals `0x0x`
Expand Down
32 changes: 16 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/cakevm/huff-neo"
rust-version = "1.84"
version = "1.0.2"
version = "1.0.3"

[workspace.dependencies]
huff-neo-codegen = { path = "crates/codegen" }
Expand Down
14 changes: 10 additions & 4 deletions crates/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,15 @@ impl Codegen {
/// Fills table JUMPDEST placeholders.
pub fn gen_table_bytecode(res: BytecodeRes) -> Result<String, CodegenError> {
if !res.unmatched_jumps.is_empty() {
let labels = res.unmatched_jumps.iter().map(|uj| uj.label.to_string()).collect::<Vec<String>>();
tracing::error!(
target: "codegen",
"Source contains unmatched jump labels \"{}\"",
res.unmatched_jumps.iter().map(|uj| uj.label.to_string()).collect::<Vec<String>>().join(", ")
labels.join(", ")
);

return Err(CodegenError {
kind: CodegenErrorKind::UnmatchedJumpLabel,
kind: CodegenErrorKind::UnmatchedJumpLabels(labels),
span: AstSpan(res.unmatched_jumps.iter().flat_map(|uj| uj.span.0.clone()).collect::<Vec<Span>>()),
token: None,
});
Expand Down Expand Up @@ -168,7 +170,11 @@ impl Codegen {
"Definition not found for Jump Table Label: \"{}\"",
label
);
return Err(CodegenError { kind: CodegenErrorKind::UnmatchedJumpLabel, span: s.span.clone(), token: None });
return Err(CodegenError {
kind: CodegenErrorKind::UnmatchedJumpLabels(vec![label.clone()]),
span: s.span.clone(),
token: None,
});
}
};
let hex = format_even_bytes(format!("{offset:02x}"));
Expand Down Expand Up @@ -363,7 +369,7 @@ impl Codegen {
let bytes = bytes.into_iter().fold(Vec::default(), |mut acc, (code_index, mut formatted_bytes)| {
// Check if a jump table exists at `code_index` (starting offset of `b`)
if let Some(jt) = jump_table.get(&code_index) {
// Loop through jumps inside of the found JumpTable
// Loop through jumps inside the found JumpTable
for jump in jt {
// Check if the jump label has been defined. If not, add `jump` to the
// unmatched jumps and define its `bytecode_index`
Expand Down
38 changes: 20 additions & 18 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,14 @@ impl<'a, 'l> Compiler<'a, 'l> {
.map(|v| Self::recurse_deps(v, &Remapper::new("./"), self.file_provider.clone(), HashSet::new()))
.collect();

// Collect Recurse Deps errors and try to resolve to the first one
let mut errors = recursed_file_sources.iter().filter_map(|rfs| rfs.as_ref().err()).collect::<Vec<&Arc<CompilerError>>>();
if !errors.is_empty() {
let error = errors.remove(0);
return Err(Arc::clone(error));
}

// Unpack recursed dependencies into FileSources
let files = recursed_file_sources.into_iter().filter_map(|fs| fs.ok()).collect::<Vec<Arc<FileSource>>>();
let mut files = vec![];
for fs in recursed_file_sources {
match fs {
Ok(f) => files.push(f),
Err(e) => return Err(e),
}
}
tracing::info!(target: "core", "COMPILER RECURSED {} FILE DEPENDENCIES", files.len());

// Parallel Compilation
Expand Down Expand Up @@ -290,15 +289,14 @@ impl<'a, 'l> Compiler<'a, 'l> {
.map(|f| Self::recurse_deps(f, &Remapper::new("./"), self.file_provider.clone(), HashSet::new()))
.collect();

// Collect Recurse Deps errors and try to resolve to the first one
let mut errors = recursed_file_sources.iter().filter_map(|rfs| rfs.as_ref().err()).collect::<Vec<&Arc<CompilerError>>>();
if !errors.is_empty() {
let error = errors.remove(0);
return Err(Arc::clone(error));
}

// Unpack recursed dependencies into FileSources
let files = recursed_file_sources.into_iter().filter_map(|fs| fs.ok()).collect::<Vec<Arc<FileSource>>>();
let mut files = vec![];
for fs in recursed_file_sources {
match fs {
Ok(f) => files.push(f),
Err(e) => return Err(e),
}
}
tracing::info!(target: "core", "COMPILER RECURSED {} FILE DEPENDENCIES", files.len());

// Parse file sources and collect ASTs in parallel
Expand Down Expand Up @@ -386,7 +384,9 @@ impl<'a, 'l> Compiler<'a, 'l> {
.0
.into_iter()
.map(|mut s| {
s.file = Some(Arc::clone(&file));
if s.file.is_none() {
s.file = Some(Arc::clone(&file));
}
s
})
.collect::<Vec<Span>>(),
Expand All @@ -412,7 +412,9 @@ impl<'a, 'l> Compiler<'a, 'l> {
.0
.into_iter()
.map(|mut s| {
s.file = Some(Arc::clone(&file));
if s.file.is_none() {
s.file = Some(Arc::clone(&file));
}
s
})
.collect::<Vec<Span>>();
Expand Down
2 changes: 1 addition & 1 deletion crates/core/tests/codegen_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ fn test_unmatched_jump_label() {
assert_eq!(
e,
CodegenError {
kind: CodegenErrorKind::UnmatchedJumpLabel,
kind: CodegenErrorKind::UnmatchedJumpLabels(vec!["err".to_string()]),
span: AstSpan(vec![
Span { start: 372, end: 375, file: None },
Span { start: 376, end: 376, file: None },
Expand Down
13 changes: 4 additions & 9 deletions crates/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ impl Parser {
// If the opcode is a push that takes a literal value, we need to parse the next
// literal
if o.is_value_push() {
match self.current_token.kind.clone() {
match self.current_token.kind {
TokenKind::Literal(val) => {
let curr_spans = vec![self.current_token.span.clone()];
tracing::info!(target: "parser", "PARSING MACRO BODY: [LITERAL: {}]", hex::encode(val));
Expand Down Expand Up @@ -616,10 +616,10 @@ impl Parser {
tracing::info!(target: "parser", "PARSING MACRO BODY: [IDENT: {}]", ident_str);
self.match_kind(TokenKind::Ident("MACRO_NAME".to_string()))?;
// Can be a macro call or label call
match self.current_token.kind.clone() {
match self.current_token.kind {
TokenKind::OpenParen => {
// Parse Macro Call
let lit_args = self.parse_macro_call()?;
let lit_args = self.parse_macro_call_args()?;
// Grab all spans following our macro invocation spam
if let Some(i) = self.spans.iter().position(|s| s.eq(&curr_spans[0])) {
curr_spans.append(&mut self.spans[(i + 1)..].to_vec());
Expand Down Expand Up @@ -728,7 +728,7 @@ impl Parser {
match self.current_token.kind.clone() {
TokenKind::OpenParen => {
// Parse Macro Call
let lit_args = self.parse_macro_call()?;
let lit_args = self.parse_macro_call_args()?;
// Grab all spans following our macro invocation spam
if let Some(i) = self.spans.iter().position(|s| s.eq(&curr_spans[0])) {
curr_spans.append(&mut self.spans[(i + 1)..].to_vec());
Expand Down Expand Up @@ -971,11 +971,6 @@ impl Parser {
Ok(value)
}

/// Parse call to a macro.
pub fn parse_macro_call(&mut self) -> Result<Vec<MacroArg>, ParserError> {
self.parse_macro_call_args()
}

/// Parse the arguments of a macro call.
pub fn parse_macro_call_args(&mut self) -> Result<Vec<MacroArg>, ParserError> {
let mut args = vec![];
Expand Down
8 changes: 4 additions & 4 deletions crates/utils/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ pub enum CodegenErrorKind {
/// Abi Generation Failure
AbiGenerationFailure,
/// Unmatched Jump
UnmatchedJumpLabel,
UnmatchedJumpLabels(Vec<String>),
/// An IO Error
IOError(String),
/// ArgCall has an unknown type
Expand Down Expand Up @@ -232,7 +232,7 @@ impl<W: Write> Report<W> for CodegenError {
write!(f.out, "Missing Error Definition for \"{ed}\"!")
}
CodegenErrorKind::AbiGenerationFailure => write!(f.out, "Abi generation failure!"),
CodegenErrorKind::UnmatchedJumpLabel => write!(f.out, "Unmatched jump label!"),
CodegenErrorKind::UnmatchedJumpLabels(labels) => write!(f.out, "Unmatched jump labels: \"{}\"!", labels.join(", ")),
CodegenErrorKind::IOError(ioe) => write!(f.out, "IO ERROR: {ioe:?}"),
CodegenErrorKind::UnkownArgcallType => write!(f.out, "Unknown Argcall Type!"),
CodegenErrorKind::MissingMacroInvocation(str) => {
Expand Down Expand Up @@ -483,8 +483,8 @@ impl fmt::Display for CompilerError {
CodegenErrorKind::MissingMacroInvocation(mmi) => {
write!(f, "\nError: Missing Macro Invocation: \"{}\"\n{}\n", mmi, ce.span.error(None))
}
CodegenErrorKind::UnmatchedJumpLabel => {
write!(f, "\nError: Unmatched Jump Label\n{}\n", ce.span.error(None))
CodegenErrorKind::UnmatchedJumpLabels(labels) => {
write!(f, "\nError: Unmatched Jump Labels: \"{}\"\n{}\n", labels.join(", "), ce.span.error(None))
}
CodegenErrorKind::UsizeConversion(_) => {
write!(f, "\nError: Usize Conversion\n{}\n", ce.span.error(None))
Expand Down
8 changes: 7 additions & 1 deletion crates/utils/src/file/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ impl Span {
.map(|s| {
if self.start >= s.len() {
// This should never happen, but currently does when the mapping from the flattened source is incorrect.
return format!("\nInternal compiler error: Start index out of range start={} len={}.", self.start, s.len());
return format!(
"\nInternal compiler error: Start index out of range file={}, start={}, end={}, len={}.",
self.file.clone().unwrap_or_default().path,
self.start,
self.end,
s.len()
);
}
let line_num = &s[0..self.start].as_bytes().iter().filter(|&&c| c == b'\n').count() + 1;
let line_start = &s[0..self.start].rfind('\n').unwrap_or(0);
Expand Down
Loading