Skip to content
This repository has been archived by the owner on Oct 20, 2024. It is now read-only.

chore: throw error with duplicated destination labels #308

Open
wants to merge 6 commits into
base: stage
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions huff_codegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ let contract = Contract {
functions: vec![],
events: vec![],
tables: vec![],
labels: vec![],
};

// Generate the main bytecode
Expand Down Expand Up @@ -149,6 +150,7 @@ let contract = Contract {
functions: vec![],
events: vec![],
tables: vec![],
labels: vec![],
};

// Generate the constructor bytecode
Expand Down
2 changes: 1 addition & 1 deletion huff_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ impl Codegen {
let circular_codesize_invocations = circular_codesize_invocations.unwrap_or(&mut ccsi);

// Loop through all intermediate bytecode representations generated from the AST
for (_ir_bytes_index, ir_byte) in ir_bytes.iter().enumerate() {
for ir_byte in ir_bytes.iter() {
let starting_offset = offset;
match &ir_byte.ty {
IRByteType::Bytes(b) => {
Expand Down
2 changes: 2 additions & 0 deletions huff_codegen/tests/abigen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ fn constructs_valid_abi() {
functions: vec![],
events: vec![],
tables: vec![],
labels: vec![],
};

// Generate the abi from the contract
Expand Down Expand Up @@ -68,6 +69,7 @@ fn missing_constructor_fails() {
functions: vec![],
events: vec![],
tables: vec![],
labels: vec![],
};

// Generate the abi from the contract
Expand Down
10 changes: 5 additions & 5 deletions huff_core/benches/huff_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn lex_erc20_from_source_benchmark(c: &mut Criterion) {
.collect();

// Recurse file deps + generate flattened source
let file_source = file_sources.get(0).unwrap();
let file_source = file_sources.first().unwrap();
let recursed_file_source =
Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider)
.unwrap();
Expand Down Expand Up @@ -48,7 +48,7 @@ fn parse_erc20_benchmark(c: &mut Criterion) {
.collect();

// Recurse file deps + generate flattened source
let file_source = file_sources.get(0).unwrap();
let file_source = file_sources.first().unwrap();
let recursed_file_source =
Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider)
.unwrap();
Expand Down Expand Up @@ -84,7 +84,7 @@ fn codegen_erc20_benchmark(c: &mut Criterion) {
.collect();

// Recurse file deps + generate flattened source
let file_source = file_sources.get(0).unwrap();
let file_source = file_sources.first().unwrap();
let recursed_file_source =
Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider)
.unwrap();
Expand Down Expand Up @@ -133,7 +133,7 @@ fn erc20_compilation_benchmark(c: &mut Criterion) {
.collect();

// Recurse file deps + generate flattened source
let file_source = file_sources.get(0).unwrap();
let file_source = file_sources.first().unwrap();
let recursed_file_source = Compiler::recurse_deps(
Arc::clone(file_source),
&files::Remapper::new("./"),
Expand Down Expand Up @@ -180,7 +180,7 @@ fn erc721_compilation_benchmark(c: &mut Criterion) {
.collect();

// Recurse file deps + generate flattened source
let file_source = file_sources.get(0).unwrap();
let file_source = file_sources.first().unwrap();
let recursed_file_source = Compiler::recurse_deps(
Arc::clone(file_source),
&files::Remapper::new("./"),
Expand Down
2 changes: 1 addition & 1 deletion huff_core/tests/erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn test_erc20_compile() {
.collect();

// Recurse file deps + generate flattened source
let file_source = file_sources.get(0).unwrap();
let file_source = file_sources.first().unwrap();
let recursed_file_source =
Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider)
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion huff_core/tests/erc721.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn test_erc721_compile() {
.collect();

// Recurse file deps + generate flattened source
let file_source = file_sources.get(0).unwrap();
let file_source = file_sources.first().unwrap();
let recursed_file_source =
Compiler::recurse_deps(Arc::clone(file_source), &files::Remapper::new("./"), file_provider)
.unwrap();
Expand Down
6 changes: 3 additions & 3 deletions huff_lexer/tests/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn parses_jump_table() {
.filter(|x| !matches!(x.kind, TokenKind::Whitespace))
.collect::<Vec<Token>>();

assert_eq!(tokens.get(0).unwrap().kind, TokenKind::Define);
assert_eq!(tokens.first().unwrap().kind, TokenKind::Define);
assert_eq!(tokens.get(1).unwrap().kind, TokenKind::JumpTable);
assert_eq!(tokens.get(2).unwrap().kind, TokenKind::Ident(String::from("JUMP_TABLE")));
assert_eq!(tokens.get(3).unwrap().kind, TokenKind::OpenParen);
Expand All @@ -30,7 +30,7 @@ fn parses_packed_jump_table() {
.filter(|x| !matches!(x.kind, TokenKind::Whitespace))
.collect::<Vec<Token>>();

assert_eq!(tokens.get(0).unwrap().kind, TokenKind::Define);
assert_eq!(tokens.first().unwrap().kind, TokenKind::Define);
assert_eq!(tokens.get(1).unwrap().kind, TokenKind::JumpTablePacked);
assert_eq!(tokens.get(2).unwrap().kind, TokenKind::Ident(String::from("JUMP_TABLE_PACKED")));
assert_eq!(tokens.get(3).unwrap().kind, TokenKind::OpenParen);
Expand All @@ -48,7 +48,7 @@ fn parses_code_table() {
.filter(|x| !matches!(x.kind, TokenKind::Whitespace))
.collect::<Vec<Token>>();

assert_eq!(tokens.get(0).unwrap().kind, TokenKind::Define);
assert_eq!(tokens.first().unwrap().kind, TokenKind::Define);
assert_eq!(tokens.get(1).unwrap().kind, TokenKind::CodeTable);
assert_eq!(tokens.get(2).unwrap().kind, TokenKind::Ident(String::from("CODE_TABLE")));
assert_eq!(tokens.get(3).unwrap().kind, TokenKind::OpenParen);
Expand Down
1 change: 1 addition & 0 deletions huff_parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ let expected_contract = Contract {
functions: vec![],
events: vec![],
tables: vec![],
labels: vec![]
};
assert_eq!(unwrapped_contract.macros, expected_contract.macros);
```
35 changes: 28 additions & 7 deletions huff_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct Parser {
impl Parser {
/// Public associated function that instantiates a Parser.
pub fn new(tokens: Vec<Token>, base: Option<String>) -> Self {
let initial_token = tokens.get(0).unwrap().clone();
let initial_token = tokens.first().unwrap().clone();
let remapper = files::Remapper::new("./");
Self { tokens, cursor: 0, current_token: initial_token, base, spans: vec![], remapper }
}
Expand All @@ -43,7 +43,7 @@ impl Parser {
///
/// PANICS if the tokens vec is empty!
pub fn reset(&mut self) {
self.current_token = self.tokens.get(0).unwrap().clone();
self.current_token = self.tokens.first().unwrap().clone();
self.cursor = 0;
}

Expand All @@ -70,7 +70,7 @@ impl Parser {
}
// Check for a decorator above a test macro
else if self.check(TokenKind::Pound) {
let m = self.parse_macro()?;
let m = self.parse_macro(&mut contract)?;
tracing::info!(target: "parser", "SUCCESSFULLY PARSED MACRO {}", m.name);
contract.macros.push(m);
}
Expand Down Expand Up @@ -102,7 +102,7 @@ impl Parser {
contract.errors.push(e);
}
TokenKind::Macro | TokenKind::Fn | TokenKind::Test => {
let m = self.parse_macro()?;
let m = self.parse_macro(&mut contract)?;
tracing::info!(target: "parser", "SUCCESSFULLY PARSED MACRO {}", m.name);
self.check_duplicate_macro(&contract, &m)?;
contract.macros.push(m);
Expand Down Expand Up @@ -186,6 +186,26 @@ impl Parser {
std::mem::discriminant(&self.current_token.kind) == std::mem::discriminant(&kind)
}

/// Checks whether the input label is unique.
/// If so, it will be added to the contract. Otherwise, an error will be returned.
fn check_duplicate_label(
&self,
contract: &mut Contract,
label: String,
) -> Result<(), ParserError> {
if contract.labels.binary_search_by(|_label| _label.cmp(&label)).is_ok() {
tracing::error!(target: "parser", "DUPLICATED LABEL NAME: {}", label);
Err(ParserError {
kind: ParserErrorKind::DuplicateLabel(label.clone()),
hint: Some(format!("Duplicated label name: \"{label}\"")),
spans: AstSpan(self.spans.clone()),
})
} else {
contract.labels.push(label);
Ok(())
}
}

/// Checks if there is a duplicate macro name
pub fn check_duplicate_macro(
&self,
Expand Down Expand Up @@ -506,7 +526,7 @@ impl Parser {
/// Parses a macro.
///
/// It should parse the following : macro MACRO_NAME(args...) = takes (x) returns (n) {...}
pub fn parse_macro(&mut self) -> Result<MacroDefinition, ParserError> {
pub fn parse_macro(&mut self, contract: &mut Contract) -> Result<MacroDefinition, ParserError> {
let mut decorator: Option<Decorator> = None;
if self.check(TokenKind::Pound) {
decorator = Some(self.parse_decorator()?);
Expand Down Expand Up @@ -538,7 +558,7 @@ impl Parser {
let macro_returns =
self.match_kind(TokenKind::Returns).map_or(Ok(0), |_| self.parse_single_arg())?;

let macro_statements: Vec<Statement> = self.parse_body()?;
let macro_statements: Vec<Statement> = self.parse_body(contract)?;

Ok(MacroDefinition::new(
macro_name,
Expand All @@ -556,7 +576,7 @@ impl Parser {
/// Parse the body of a macro.
///
/// Only HEX, OPCODES, labels, builtins, and MACRO calls should be authorized.
pub fn parse_body(&mut self) -> Result<Vec<Statement>, ParserError> {
pub fn parse_body(&mut self, contract: &mut Contract) -> Result<Vec<Statement>, ParserError> {
let mut statements: Vec<Statement> = Vec::new();
self.match_kind(TokenKind::OpenBrace)?;
tracing::info!(target: "parser", "PARSING MACRO BODY");
Expand Down Expand Up @@ -653,6 +673,7 @@ impl Parser {
TokenKind::Label(l) => {
let mut curr_spans = vec![self.current_token.span.clone()];
self.consume();
self.check_duplicate_label(contract, l.to_string())?;
let inner_statements: Vec<Statement> = self.parse_label()?;
inner_statements
.iter()
Expand Down
28 changes: 27 additions & 1 deletion huff_parser/tests/labels.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use huff_lexer::*;
use huff_parser::*;
use huff_utils::{evm::Opcode, prelude::*};
use huff_utils::{error::ParserErrorKind, evm::Opcode, prelude::*};

#[test]
fn multiline_labels() {
Expand Down Expand Up @@ -433,3 +433,29 @@ pub fn builtins_under_labels() {
assert_eq!(s.span, md_expected.statements[i].span);
}
}

#[test]
fn duplicated_labels() {
let source = r#"
#define macro MAIN() = takes(0) returns(0) {
cool_label jump
cool_label jump

cool_label: 0x00
dup_label: 0x00
dup_label: 0x00
}
"#;
let flattened_source = FullFileSource { source, file: None, spans: vec![] };
let lexer = Lexer::new(flattened_source.source);
let tokens = lexer.into_iter().map(|x| x.unwrap()).collect::<Vec<Token>>();
let mut parser = Parser::new(tokens, None);

// Grab the first macro
let parse_result = parser.parse();
assert!(parse_result.is_err());
assert_eq!(
parse_result.unwrap_err().kind,
ParserErrorKind::DuplicateLabel("dup_label".to_string())
);
}
5 changes: 3 additions & 2 deletions huff_utils/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
//! }],
//! events: vec![],
//! tables: vec![],
//! labels: vec![],
//! };
//!
//! // Create an ABI using that generate contract
Expand Down Expand Up @@ -78,7 +79,7 @@ impl From<ast::Contract> for Abi {
.filter(|m| m.name.to_lowercase() == "constructor")
.cloned()
.collect::<Vec<ast::FunctionDefinition>>()
.get(0)
.first()
.map(|func| Constructor {
inputs: func
.inputs
Expand All @@ -97,7 +98,7 @@ impl From<ast::Contract> for Abi {
.filter(|m| m.name == "CONSTRUCTOR")
.cloned()
.collect::<Vec<ast::MacroDefinition>>()
.get(0)
.first()
.map(|func| Constructor {
inputs: func
.parameters
Expand Down
12 changes: 7 additions & 5 deletions huff_utils/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ pub struct Contract {
pub events: Vec<EventDefinition>,
/// Tables
pub tables: Vec<TableDefinition>,
/// Labels
pub labels: Vec<String>,
}

impl Contract {
Expand Down Expand Up @@ -180,7 +182,7 @@ impl Contract {
.iter()
.filter(|pointer| pointer.0.eq(&c.name))
.collect::<Vec<&(String, [u8; 32])>>()
.get(0)
.first()
{
Some(p) => {
*c = ConstantDefinition {
Expand Down Expand Up @@ -261,7 +263,7 @@ impl Contract {
.iter()
.filter(|md| md.name.eq(&mi.macro_name))
.collect::<Vec<&MacroDefinition>>()
.get(0)
.first()
{
Some(&md) => {
if md.name.eq("CONSTRUCTOR") {
Expand Down Expand Up @@ -291,7 +293,7 @@ impl Contract {
.iter()
.filter(|md| md.name.eq(name))
.collect::<Vec<&MacroDefinition>>()
.get(0)
.first()
{
Some(&md) => {
if md.name.eq("CONSTRUCTOR") {
Expand Down Expand Up @@ -348,7 +350,7 @@ impl Contract {
.iter()
.filter(|pointer| pointer.0.eq(const_name))
.collect::<Vec<&(String, [u8; 32])>>()
.get(0)
.first()
.is_none()
{
tracing::debug!(target: "ast", "No storage pointer already set for \"{}\"!", const_name);
Expand All @@ -360,7 +362,7 @@ impl Contract {
.iter()
.filter(|c| c.name.eq(const_name))
.collect::<Vec<&ConstantDefinition>>()
.get(0)
.first()
{
Some(c) => {
let new_value = match c.value {
Expand Down
10 changes: 10 additions & 0 deletions huff_utils/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ pub enum ParserErrorKind {
InvalidDecoratorFlag(String),
/// Invalid decorator flag argument
InvalidDecoratorFlagArg(TokenKind),
/// Duplicate label
DuplicateLabel(String),
/// Duplicate MACRO
DuplicateMacro(String),
}
Expand Down Expand Up @@ -490,6 +492,14 @@ impl fmt::Display for CompilerError {
pe.spans.error(pe.hint.as_ref())
)
}
ParserErrorKind::DuplicateLabel(label) => {
write!(
f,
"\nError: Duplicate label: \"{}\" \n{}\n",
label,
pe.spans.error(pe.hint.as_ref())
)
}
ParserErrorKind::DuplicateMacro(mn) => {
write!(
f,
Expand Down