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

Commit

Permalink
Merge pull request #204 from huff-language/ab/philogy_fixes
Browse files Browse the repository at this point in the history
add checks for evm types as arg names
  • Loading branch information
refcell authored Aug 30, 2022
2 parents 57ef0f0 + 06d7bbe commit 004da95
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 14 deletions.
13 changes: 10 additions & 3 deletions huff_lexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ impl<'a> Iterator for Lexer<'a> {
}
}

// Check for macro keyword
// Check for free storage pointer builtin
let fsp = "FREE_STORAGE_POINTER";
let token_length = fsp.len() - 1;
let peeked = self.peek_n_chars(token_length);
Expand Down Expand Up @@ -503,8 +503,15 @@ impl<'a> Iterator for Lexer<'a> {
self.dyn_consume(|c| c.is_alphanumeric() || *c == '[' || *c == ']');
// got a type at this point, we have to know which
let raw_type: String = self.slice();
// check for arrays first
if EVM_TYPE_ARRAY_REGEX.is_match(&raw_type) {

// Check if calldata, memory, or storage
if raw_type == TokenKind::Calldata.to_string() {
found_kind = Some(TokenKind::Calldata);
} else if raw_type == TokenKind::Memory.to_string() {
found_kind = Some(TokenKind::Memory);
} else if raw_type == TokenKind::Storage.to_string() {
found_kind = Some(TokenKind::Storage);
} else if EVM_TYPE_ARRAY_REGEX.is_match(&raw_type) {
// split to get array size and type
// TODO: support multi-dimensional arrays
let words: Vec<String> = Regex::new(r"\[")
Expand Down
71 changes: 67 additions & 4 deletions huff_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@ impl Parser {
) -> Result<Vec<Argument>, ParserError> {
let mut args: Vec<Argument> = Vec::new();
self.match_kind(TokenKind::OpenParen)?;
let mut on_type = true;
tracing::debug!(target: "parser", "PARSING ARGs: {:?}", self.current_token.kind);
while !self.check(TokenKind::CloseParen) {
if is_builtin {
Expand All @@ -801,6 +802,7 @@ impl Parser {
arg_type: None,
indexed: false,
span: AstSpan(vec![self.current_token.span.clone()]),
arg_location: None,
});

self.consume();
Expand All @@ -812,8 +814,9 @@ impl Parser {
// present.
if let TokenKind::Literal(l) = &self.current_token.kind {
args.push(Argument {
name: Some(bytes32_to_string(l, false)), /* Place the literal in the
* "name" field */
// Place literal in the "name" field
name: Some(bytes32_to_string(l, false)),
arg_location: None,
arg_type: None,
indexed: false,
span: AstSpan(vec![self.current_token.span.clone()]),
Expand All @@ -837,17 +840,73 @@ impl Parser {
arg_spans.push(self.current_token.span.clone());
self.consume(); // consume "indexed" keyword
}
on_type = false;
}

// It can also be a data location
match &self.current_token.kind {
TokenKind::Calldata => {
arg.arg_location = Some(ArgumentLocation::Calldata);
arg_spans.push(self.current_token.span.clone());
self.consume();
}
TokenKind::Memory => {
arg.arg_location = Some(ArgumentLocation::Memory);
arg_spans.push(self.current_token.span.clone());
self.consume();
}
TokenKind::Storage => {
arg.arg_location = Some(ArgumentLocation::Storage);
arg_spans.push(self.current_token.span.clone());
self.consume();
}
_ => {}
}

// name comes second (is optional)
if select_name && self.check(TokenKind::Ident("x".to_string())) {
if select_name &&
(self.check(TokenKind::Ident("x".to_string())) ||
self.check(TokenKind::PrimitiveType(PrimitiveEVMType::Address)))
{
// We need to check if the name is a keyword - not the type
if !on_type {
// Check for reserved primitive type keyword use and throw an error if so
match self.current_token.kind.clone() {
TokenKind::Ident(arg_str) => {
if PrimitiveEVMType::try_from(arg_str.clone()).is_ok() {
return Err(ParserError {
kind: ParserErrorKind::InvalidTypeAsArgumentName(
self.current_token.kind.clone(),
),
hint: Some(format!(
"Argument names cannot be EVM types: {}",
arg_str
)),
spans: AstSpan(vec![self.current_token.span.clone()]),
})
}
}
TokenKind::PrimitiveType(ty) => {
return Err(ParserError {
kind: ParserErrorKind::InvalidTypeAsArgumentName(
self.current_token.kind.clone(),
),
hint: Some(format!("Argument names cannot be EVM types: {}", ty)),
spans: AstSpan(vec![self.current_token.span.clone()]),
})
}
_ => { /* continue, valid string */ }
}
}
arg_spans.push(self.current_token.span.clone());
arg.name = Some(self.match_kind(TokenKind::Ident("x".to_string()))?.to_string())
arg.name = Some(self.match_kind(TokenKind::Ident("x".to_string()))?.to_string());
on_type = !on_type;
}

// multiple args possible
if self.check(TokenKind::Comma) {
self.consume();
on_type = true;
}

// If both arg type and arg name are none, we didn't consume anything
Expand Down Expand Up @@ -907,6 +966,10 @@ impl Parser {
args.push(MacroArg::Ident(ident));
self.consume();
}
TokenKind::Calldata => {
args.push(MacroArg::Ident("calldata".to_string()));
self.consume();
}
TokenKind::LeftAngle => {
// Passed into the Macro Call like:
// GET_SLOT_FROM_KEY(<mem_ptr>) // [slot]
Expand Down
3 changes: 2 additions & 1 deletion huff_parser/tests/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ fn test_parses_custom_error() {
arg_type: Some(String::from("uint256")),
name: None,
indexed: false,
span: AstSpan(vec![Span { start: 24, end: 31, file: None }])
span: AstSpan(vec![Span { start: 24, end: 31, file: None }]),
arg_location: None,
}],
span: AstSpan(vec![
Span { start: 0, end: 7, file: None },
Expand Down
74 changes: 74 additions & 0 deletions huff_parser/tests/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,74 @@ use huff_lexer::*;
use huff_parser::*;
use huff_utils::{ast::Event, prelude::*};

#[test]
fn test_prefix_event_arg_names_with_reserved_keywords() {
let source: &str = "#define event TestEvent(bytes4 indexed interfaceId, uint256 uintTest, bool stringMe, string boolean)";
let flattened_source = FullFileSource { source, file: None, spans: vec![] };
let lexer = Lexer::new(flattened_source);
let tokens = lexer.into_iter().map(|x| x.unwrap()).collect::<Vec<Token>>();
let expected_tokens: Vec<Token> = vec![
Token { kind: TokenKind::Define, span: Span { start: 0, end: 7, file: None } },
Token { kind: TokenKind::Whitespace, span: Span { start: 7, end: 8, file: None } },
Token { kind: TokenKind::Event, span: Span { start: 8, end: 13, file: None } },
Token { kind: TokenKind::Whitespace, span: Span { start: 13, end: 14, file: None } },
Token {
kind: TokenKind::Ident("TestEvent".to_string()),
span: Span { start: 14, end: 23, file: None },
},
Token { kind: TokenKind::OpenParen, span: Span { start: 23, end: 24, file: None } },
Token {
kind: TokenKind::PrimitiveType(PrimitiveEVMType::Bytes(4)),
span: Span { start: 24, end: 30, file: None },
},
Token { kind: TokenKind::Whitespace, span: Span { start: 30, end: 31, file: None } },
Token { kind: TokenKind::Indexed, span: Span { start: 31, end: 38, file: None } },
Token { kind: TokenKind::Whitespace, span: Span { start: 38, end: 39, file: None } },
Token {
kind: TokenKind::Ident("interfaceId".to_string()),
span: Span { start: 39, end: 50, file: None },
},
Token { kind: TokenKind::Comma, span: Span { start: 50, end: 51, file: None } },
Token { kind: TokenKind::Whitespace, span: Span { start: 51, end: 52, file: None } },
Token {
kind: TokenKind::PrimitiveType(PrimitiveEVMType::Uint(256)),
span: Span { start: 52, end: 59, file: None },
},
Token { kind: TokenKind::Whitespace, span: Span { start: 59, end: 60, file: None } },
Token {
kind: TokenKind::Ident("uintTest".to_string()),
span: Span { start: 60, end: 68, file: None },
},
Token { kind: TokenKind::Comma, span: Span { start: 68, end: 69, file: None } },
Token { kind: TokenKind::Whitespace, span: Span { start: 69, end: 70, file: None } },
Token {
kind: TokenKind::PrimitiveType(PrimitiveEVMType::Bool),
span: Span { start: 70, end: 74, file: None },
},
Token { kind: TokenKind::Whitespace, span: Span { start: 74, end: 75, file: None } },
Token {
kind: TokenKind::Ident("stringMe".to_string()),
span: Span { start: 75, end: 83, file: None },
},
Token { kind: TokenKind::Comma, span: Span { start: 83, end: 84, file: None } },
Token { kind: TokenKind::Whitespace, span: Span { start: 84, end: 85, file: None } },
Token {
kind: TokenKind::PrimitiveType(PrimitiveEVMType::String),
span: Span { start: 85, end: 91, file: None },
},
Token { kind: TokenKind::Whitespace, span: Span { start: 91, end: 92, file: None } },
Token {
kind: TokenKind::Ident("boolean".to_string()),
span: Span { start: 92, end: 99, file: None },
},
Token { kind: TokenKind::CloseParen, span: Span { start: 99, end: 100, file: None } },
Token { kind: TokenKind::Eof, span: Span { start: 100, end: 100, file: None } },
];
assert_eq!(expected_tokens, tokens);
let mut parser = Parser::new(tokens, None);
parser.parse().unwrap();
}

#[test]
fn test_parse_event() {
let sources = [
Expand All @@ -14,6 +82,7 @@ fn test_parse_event() {
arg_type: Some(String::from("uint256")),
name: Some(String::from("a")),
indexed: true,
arg_location: None,
span: AstSpan(vec![
// "uint256"
Span { start: 24, end: 31, file: None },
Expand All @@ -27,6 +96,7 @@ fn test_parse_event() {
arg_type: Some(String::from("uint8")),
name: None,
indexed: true,
arg_location: None,
span: AstSpan(vec![
// "uint8"
Span { start: 42, end: 47, file: None },
Expand Down Expand Up @@ -74,6 +144,7 @@ fn test_parse_event() {
arg_type: Some(String::from("uint256")),
name: None,
indexed: false,
arg_location: None,
span: AstSpan(vec![
// "uint256"
Span { start: 24, end: 31, file: None },
Expand All @@ -83,6 +154,7 @@ fn test_parse_event() {
arg_type: Some(String::from("uint8")),
name: Some(String::from("b")),
indexed: false,
arg_location: None,
span: AstSpan(vec![
// "uint8"
Span { start: 32, end: 37, file: None },
Expand Down Expand Up @@ -126,6 +198,7 @@ fn test_parse_event() {
arg_type: Some(String::from("uint256")),
name: None,
indexed: true,
arg_location: None,
span: AstSpan(vec![
// "uint256"
Span { start: 24, end: 31, file: None },
Expand All @@ -137,6 +210,7 @@ fn test_parse_event() {
arg_type: Some(String::from("uint8")),
name: None,
indexed: false,
arg_location: None,
span: AstSpan(vec![
// "uint8"
Span { start: 40, end: 45, file: None },
Expand Down
Loading

0 comments on commit 004da95

Please sign in to comment.