Skip to content

Commit

Permalink
support string literals in version pragmas
Browse files Browse the repository at this point in the history
  • Loading branch information
OmarTawfik committed Feb 21, 2024
1 parent 0151086 commit 0babd73
Show file tree
Hide file tree
Showing 17 changed files with 182 additions and 84 deletions.
5 changes: 5 additions & 0 deletions .changeset/tidy-vans-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomicfoundation/slang": patch
---

support string literals in version pragmas
6 changes: 4 additions & 2 deletions crates/solidity/inputs/language/src/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,10 @@ codegen_language_macros::compile!(Language(
]
)
],
primary_expressions =
[PrimaryExpression(reference = VersionPragmaSpecifier)]
primary_expressions = [
PrimaryExpression(reference = AsciiStringLiteral),
PrimaryExpression(reference = VersionPragmaSpecifier)
]
),
Separated(
name = VersionPragmaSpecifier,
Expand Down

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

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

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

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

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

1 change: 1 addition & 0 deletions crates/solidity/outputs/spec/generated/grammar.ebnf

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

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file is generated automatically by infrastructure scripts. Please don't edit by hand.

Source: >
1 │ solidity "1.2.3" │ 0..16
Errors: []

Tree:
- (VersionPragma): # 'solidity "1.2.3"\n' (0..17)
- (solidity_keyword꞉ SolidityKeyword): "solidity" # (0..8)
- (expressions꞉ VersionPragmaExpressions): # ' "1.2.3"\n' (8..17)
- (item꞉ VersionPragmaExpression): # ' "1.2.3"\n' (8..17)
- (LeadingTrivia) ► (Whitespace): " " # (8..9)
- (variant꞉ AsciiStringLiteral): '"1.2.3"' # (9..16)
- (TrailingTrivia) ► (EndOfLine): "\n" # (16..17)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
solidity "1.2.3"
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# This file is generated automatically by infrastructure scripts. Please don't edit by hand.

Source: >
1 │ solidity ^"1.2.3" || "4.5.6"-"7.8.9" │ 0..36
Errors: []

Tree:
- (VersionPragma): # 'solidity ^"1.2.3" || "4.5.6"-"7.8.9"\n' (0..37)
- (solidity_keyword꞉ SolidityKeyword): "solidity" # (0..8)
- (expressions꞉ VersionPragmaExpressions): # ' ^"1.2.3" || "4.5.6"-"7.8.9"\n' (8..37)
- (item꞉ VersionPragmaExpression) ► (variant꞉ VersionPragmaOrExpression): # ' ^"1.2.3" || "4.5.6"-"7.8.9"\n' (8..37)
- (left_operand꞉ VersionPragmaExpression) ► (variant꞉ VersionPragmaPrefixExpression): # ' ^"1.2.3"' (8..17)
- (LeadingTrivia) ► (Whitespace): " " # (8..9)
- (operator꞉ Caret): "^" # (9..10)
- (operand꞉ VersionPragmaExpression) ► (variant꞉ AsciiStringLiteral): '"1.2.3"' # (10..17)
- (LeadingTrivia) ► (Whitespace): " " # (17..18)
- (operator꞉ BarBar): "||" # (18..20)
- (right_operand꞉ VersionPragmaExpression) ► (variant꞉ VersionPragmaRangeExpression): # ' "4.5.6"-"7.8.9"\n' (20..37)
- (left_operand꞉ VersionPragmaExpression): # ' "4.5.6"' (20..28)
- (LeadingTrivia) ► (Whitespace): " " # (20..21)
- (variant꞉ AsciiStringLiteral): '"4.5.6"' # (21..28)
- (operator꞉ Minus): "-" # (28..29)
- (right_operand꞉ VersionPragmaExpression): # '"7.8.9"\n' (29..37)
- (variant꞉ AsciiStringLiteral): '"7.8.9"' # (29..36)
- (TrailingTrivia) ► (EndOfLine): "\n" # (36..37)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
solidity ^"1.2.3" || "4.5.6"-"7.8.9"
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file is generated automatically by infrastructure scripts. Please don't edit by hand.

Source: >
1 │ solidity '1.2.3' │ 0..16
Errors: []

Tree:
- (VersionPragma): # "solidity '1.2.3'\n" (0..17)
- (solidity_keyword꞉ SolidityKeyword): "solidity" # (0..8)
- (expressions꞉ VersionPragmaExpressions): # " '1.2.3'\n" (8..17)
- (item꞉ VersionPragmaExpression): # " '1.2.3'\n" (8..17)
- (LeadingTrivia) ► (Whitespace): " " # (8..9)
- (variant꞉ AsciiStringLiteral): "'1.2.3'" # (9..16)
- (TrailingTrivia) ► (EndOfLine): "\n" # (16..17)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
solidity '1.2.3'
147 changes: 69 additions & 78 deletions crates/solidity/testing/utils/src/version_pragmas/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ mod tests;

use std::str::FromStr;

use anyhow::{bail, ensure, Context, Result};
use anyhow::{bail, Context, Result};
use semver::{Comparator, Op, Version};
use slang_solidity::cst::{NamedNode, Node};
use slang_solidity::kinds::RuleKind;
use slang_solidity::cst::Node;
use slang_solidity::kinds::{RuleKind, TokenKind};
use slang_solidity::language::Language;

use crate::node_extensions::NodeExtensions;
Expand Down Expand Up @@ -43,83 +43,74 @@ pub fn extract_version_pragmas(
Ok(pragmas)
}

fn extract_pragma(expression_node: &Node) -> Result<VersionPragma> {
let Node::Rule(expression_rule) = expression_node else {
bail!("Expected rule: {expression_node:?}")
};

ensure!(
expression_rule.kind == RuleKind::VersionPragmaExpression,
"Expected VersionPragmaExpression: {expression_rule:?}",
);

let inner_expression = match &expression_rule.children[..] {
[NamedNode {
node: Node::Rule(rule),
..
}] => rule,
[NamedNode {
node: Node::Token(token),
..
}] => bail!("Expected rule: {token:?}"),
_ => unreachable!("Expected single child: {expression_rule:?}"),
};

let inner_children: Vec<_> = inner_expression
.children
.iter()
.filter(|child| !child.is_trivia())
.collect();

match inner_expression.kind {
RuleKind::VersionPragmaOrExpression => {
let [left, NamedNode {
name: _,
node: Node::Token(_op),
}, right] = &inner_children[..]
else {
bail!("Expected 3 children: {inner_expression:?}");
};
let left = extract_pragma(left)?;
let right = extract_pragma(right)?;

Ok(VersionPragma::or(left, right))
fn extract_pragma(node: &Node) -> Result<VersionPragma> {
match node {
Node::Rule(rule) => {
let children = rule
.children
.iter()
.filter(|child| !child.is_trivia())
.collect::<Vec<_>>();

match (rule.kind, &children[..]) {
(RuleKind::VersionPragmaExpression, [inner]) => extract_pragma(inner),

(RuleKind::VersionPragmaOrExpression, [left, op, right])
if op.is_token_with_kind(TokenKind::BarBar) =>
{
let left = extract_pragma(left)?;
let right = extract_pragma(right)?;

Ok(VersionPragma::or(left, right))
}

(RuleKind::VersionPragmaRangeExpression, [left, op, right])
if op.is_token_with_kind(TokenKind::Minus) =>
{
let mut left = extract_pragma(left)?.into_comparator()?;
let mut right = extract_pragma(right)?.into_comparator()?;

// Simulate solc bug:
// https://github.com/ethereum/solidity/issues/13920
left.op = Op::GreaterEq;
right.op = Op::LessEq;

Ok(VersionPragma::and(
VersionPragma::single(left),
VersionPragma::single(right),
))
}

(RuleKind::VersionPragmaPrefixExpression, _) => {
let value = rule.extract_non_trivia();
let comparator = Comparator::from_str(&value)?;

Ok(VersionPragma::single(comparator))
}

(RuleKind::VersionPragmaSpecifier, _) => {
let value = rule.extract_non_trivia();
let comparator = Comparator::from_str(&format!("={value}"))?;

Ok(VersionPragma::single(comparator))
}

(kind, children) => {
bail!("Unexpected rule kind '{kind}' and children: {children:#?}")
}
}
}
RuleKind::VersionPragmaRangeExpression => {
let [left, NamedNode {
name: _,
node: Node::Token(_op),
}, right] = &inner_children[..]
else {
bail!("Expected 3 children: {inner_expression:?}");
};

let mut left = extract_pragma(left)?.comparator()?;
let mut right = extract_pragma(right)?.comparator()?;

// Simulate solc bug:
// https://github.com/ethereum/solidity/issues/13920
left.op = Op::GreaterEq;
right.op = Op::LessEq;

Ok(VersionPragma::and(
VersionPragma::single(left),
VersionPragma::single(right),
))
}
RuleKind::VersionPragmaPrefixExpression => {
let value = inner_expression.extract_non_trivia();
let comparator = Comparator::from_str(&value)?;

Ok(VersionPragma::single(comparator))
}
RuleKind::VersionPragmaSpecifier => {
let specifier = inner_expression.extract_non_trivia();
let comparator = Comparator::from_str(&format!("={specifier}"))?;
Node::Token(token) => match token.kind {
TokenKind::AsciiStringLiteral => {
let value = token.text.trim_matches('"').trim_matches('\'');
let comparator = Comparator::from_str(&format!("={value}"))?;

Ok(VersionPragma::single(comparator))
}
_ => bail!("Unexpected inner expression: {inner_expression:?}"),
Ok(VersionPragma::single(comparator))
}

_ => bail!("Unexpected token: {token:#?}"),
},
}
}

Expand Down Expand Up @@ -151,7 +142,7 @@ impl VersionPragma {
}
}

fn comparator(self) -> Result<Comparator> {
fn into_comparator(self) -> Result<Comparator> {
match self {
Self::Single(comparator) => Ok(comparator),
_ => bail!("Expected Single Comparator: {self:?}"),
Expand Down
14 changes: 14 additions & 0 deletions crates/solidity/testing/utils/src/version_pragmas/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ fn nested_expressions() -> Result<()> {
)
}

#[test]
fn string_literals() -> Result<()> {
test_aux(
&[
"0.8.0", "0.8.1", "0.8.2", "0.8.3", "0.8.4", "0.8.5", "0.8.6", "0.8.7", "0.8.8",
"0.8.9",
],
r#"
pragma solidity "0.8.4" - '0.8.6';
"#,
&["0.8.4", "0.8.5", "0.8.6"],
)
}

fn test_aux(all_versions: &[&str], source: &str, expected: &[&str]) -> Result<()> {
let all_versions = all_versions
.iter()
Expand Down

0 comments on commit 0babd73

Please sign in to comment.