diff --git a/crates/codegen-v2/parser/src/lexer/builder.rs b/crates/codegen-v2/parser/src/lexer/builder.rs index b5964ca75a..b3683a6b5d 100644 --- a/crates/codegen-v2/parser/src/lexer/builder.rs +++ b/crates/codegen-v2/parser/src/lexer/builder.rs @@ -95,16 +95,22 @@ impl LexerModelBuilder { } Lexeme::Keyword { kind, reserved, .. } => { if match reserved { - None => true, - Some(VersionSpecifier::Never) => false, - Some(VersionSpecifier::From { .. }) => true, - Some(VersionSpecifier::Till { .. }) => true, - Some(VersionSpecifier::Range { .. }) => true, + VersionSpecifier::Always => true, + VersionSpecifier::Never => false, + VersionSpecifier::From { .. } => true, + VersionSpecifier::Till { .. } => true, + VersionSpecifier::Range { .. } => true, } { kinds.insert(format!("{kind}_Reserved")); } - if reserved.is_some() { + if match reserved { + VersionSpecifier::Always => false, + VersionSpecifier::Never => true, + VersionSpecifier::From { .. } => true, + VersionSpecifier::Till { .. } => true, + VersionSpecifier::Range { .. } => true, + } { kinds.insert(format!("{kind}_Unreserved")); } } @@ -126,7 +132,7 @@ impl LexerModelBuilder { item.definitions.iter().map(|def| Lexeme::Keyword { kind: item.name.to_string(), regex: self.convert_keyword_value(&def.value), - reserved: def.reserved.clone(), + reserved: def.reserved.clone().unwrap_or_default(), }) } diff --git a/crates/codegen-v2/parser/src/lexer/mod.rs b/crates/codegen-v2/parser/src/lexer/mod.rs index 34b974c6e5..530dcbb332 100644 --- a/crates/codegen-v2/parser/src/lexer/mod.rs +++ b/crates/codegen-v2/parser/src/lexer/mod.rs @@ -39,6 +39,6 @@ pub enum Lexeme { Keyword { kind: String, regex: String, - reserved: Option, + reserved: VersionSpecifier, }, } diff --git a/crates/language-v2/definition/src/compiler/analysis/p2_version_specifiers/mod.rs b/crates/language-v2/definition/src/compiler/analysis/p2_version_specifiers/mod.rs index 763861f7b3..5757e705bb 100644 --- a/crates/language-v2/definition/src/compiler/analysis/p2_version_specifiers/mod.rs +++ b/crates/language-v2/definition/src/compiler/analysis/p2_version_specifiers/mod.rs @@ -263,6 +263,7 @@ fn check_version_specifier( }; match &**specifier { + SpannedVersionSpecifier::Always => {} SpannedVersionSpecifier::Never => {} SpannedVersionSpecifier::From { from } => { check_version(analysis, from); diff --git a/crates/language-v2/definition/src/compiler/utils/version_set.rs b/crates/language-v2/definition/src/compiler/utils/version_set.rs index 03d8eb48eb..ab1af48c85 100644 --- a/crates/language-v2/definition/src/compiler/utils/version_set.rs +++ b/crates/language-v2/definition/src/compiler/utils/version_set.rs @@ -28,6 +28,9 @@ impl VersionSet { language: &SpannedLanguage, ) { match specifier { + SpannedVersionSpecifier::Always => { + self.add_version_range(&language.versions[0], &MAX_VERSION); + } SpannedVersionSpecifier::Never => { // Do nothing. } diff --git a/crates/language-v2/definition/src/model/item.rs b/crates/language-v2/definition/src/model/item.rs index 1ada2328df..bc4923b352 100644 --- a/crates/language-v2/definition/src/model/item.rs +++ b/crates/language-v2/definition/src/model/item.rs @@ -9,6 +9,9 @@ use crate::model::{ StructItem, TokenItem, TriviaItem, }; +/// An item is the smallest unit of a language definition. +/// +/// It represents both terminals and nonterminals. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[derive_spanned_type(Clone, Debug, EnumDiscriminants, ParseInputTokens, WriteOutputTokens)] #[serde(tag = "type")] diff --git a/crates/language-v2/definition/src/model/manifest.rs b/crates/language-v2/definition/src/model/manifest.rs index 862ff2a138..52d268e265 100644 --- a/crates/language-v2/definition/src/model/manifest.rs +++ b/crates/language-v2/definition/src/model/manifest.rs @@ -1,5 +1,4 @@ use std::collections::BTreeSet; -use std::path::PathBuf; use indexmap::IndexSet; use language_v2_internal_macros::{derive_spanned_type, ParseInputTokens, WriteOutputTokens}; @@ -9,21 +8,29 @@ use serde::{Deserialize, Serialize}; use super::BuiltIn; use crate::model::{BuiltInContext, Field, Identifier, Item, TriviaParser, VersionSpecifier}; +/// A representation of a Language definition #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)] pub struct Language { + /// The name of the language pub name: Identifier, - pub binding_rules_file: PathBuf, - + /// Each language must have a single root item pub root_item: Identifier, + /// The leading trivia parser pub leading_trivia: TriviaParser, + + /// The trailing trivia parser pub trailing_trivia: TriviaParser, + /// The supported versions of the language pub versions: IndexSet, + /// The sections of the language pub sections: Vec
, + + /// The built-in contexts pub built_ins: Vec, } @@ -57,7 +64,7 @@ impl Language { let mut add_spec = |spec: &Option| { if let Some(spec) = spec { - res.extend(spec.versions().cloned()); + res.extend(spec.breaking_versions().cloned()); } }; @@ -130,7 +137,7 @@ impl Language { let mut add_spec = |spec: &Option| { if let Some(spec) = spec { - res.extend(spec.versions().cloned()); + res.extend(spec.breaking_versions().cloned()); } }; @@ -160,6 +167,7 @@ impl Language { } } +/// A section is a named container for topics, used for organizing the large grammar definition in user documentation. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)] pub struct Section { @@ -174,6 +182,7 @@ impl Section { } } +/// A topic is a named container for items, used for organizing the large grammar definition in user documentation. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)] pub struct Topic { diff --git a/crates/language-v2/definition/src/model/nonterminals/field.rs b/crates/language-v2/definition/src/model/nonterminals/field.rs index 37f8ed5392..f371c83a08 100644 --- a/crates/language-v2/definition/src/model/nonterminals/field.rs +++ b/crates/language-v2/definition/src/model/nonterminals/field.rs @@ -3,12 +3,28 @@ use serde::{Deserialize, Serialize}; use crate::model::{Identifier, VersionSpecifier}; +/// Error recovery for fields, this is used by the parser to recover in case of missing or unexpected tokens. +/// +/// Note: Some nonterminals have both a terminator and delimiters, ie `DoWhileStatement`. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)] pub struct FieldsErrorRecovery { + /// Error recovery happens at a terminator. + /// + /// For example `PragmaDirective` has a `Semicolon` terminator, that could + /// be used to recover from an invalid `pragma` directive like + /// ``` + /// pragma soldity ^0.8.0; + /// ``` #[serde(skip_serializing_if = "Option::is_none")] pub terminator: Option, + /// Error recovery happens at delimiters. + /// + /// For example `TupleExpression` has a `OpenParen` and `CloseParen` delimiters, + /// that could be used to recover from an invalid tuple like + /// `(pragma, bar)` + /// ~~~~~ -> This is not a valid expression #[serde(skip_serializing_if = "Option::is_none")] pub delimiters: Option, } @@ -29,6 +45,9 @@ pub struct FieldDelimiters { pub terminals_matched_acceptance_threshold: Option, } +/// A `Field` of a nonterminal that can be either required or optional. +/// +/// Note: `Optional` fields are versioned, `Required` fields are not. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)] #[serde(tag = "type")] diff --git a/crates/language-v2/definition/src/model/nonterminals/struct_.rs b/crates/language-v2/definition/src/model/nonterminals/struct_.rs index c17ae938ea..f33daa7a41 100644 --- a/crates/language-v2/definition/src/model/nonterminals/struct_.rs +++ b/crates/language-v2/definition/src/model/nonterminals/struct_.rs @@ -4,17 +4,22 @@ use serde::{Deserialize, Serialize}; use crate::model::{Field, FieldsErrorRecovery, Identifier, VersionSpecifier}; +/// A `StructItem` is a nonterminal that can have fields. +/// It roughly corresponds to a sequence of `Item`s. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] #[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)] pub struct StructItem { pub name: Identifier, + /// Whether the struct is enabled #[serde(skip_serializing_if = "Option::is_none")] pub enabled: Option, + /// Error recovery information if this struct supports it #[serde(skip_serializing_if = "Option::is_none")] pub error_recovery: Option, + /// The fields of the struct, in the order they should appear in the source code #[serde(with = "indexmap::map::serde_seq")] pub fields: IndexMap, } diff --git a/crates/language-v2/definition/src/model/utils/version_specifier.rs b/crates/language-v2/definition/src/model/utils/version_specifier.rs index 60247c9b10..aef0851e2c 100644 --- a/crates/language-v2/definition/src/model/utils/version_specifier.rs +++ b/crates/language-v2/definition/src/model/utils/version_specifier.rs @@ -2,19 +2,29 @@ use language_v2_internal_macros::{derive_spanned_type, ParseInputTokens, WriteOu use semver::Version; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[derive_spanned_type(Clone, Debug, ParseInputTokens, WriteOutputTokens)] #[serde(tag = "type")] pub enum VersionSpecifier { + #[default] + Always, Never, - From { from: Version }, - Till { till: Version }, - Range { from: Version, till: Version }, + From { + from: Version, + }, + Till { + till: Version, + }, + Range { + from: Version, + till: Version, + }, } impl VersionSpecifier { pub fn contains(&self, version: &Version) -> bool { match self { + VersionSpecifier::Always => true, VersionSpecifier::Never => false, VersionSpecifier::From { from } => from <= version, VersionSpecifier::Till { till } => version < till, @@ -23,9 +33,9 @@ impl VersionSpecifier { } /// Returns an iterator over the versions specified as the upper and lower bound. - pub fn versions(&self) -> impl Iterator { + pub fn breaking_versions(&self) -> impl Iterator { match self { - VersionSpecifier::Never => [None, None], + VersionSpecifier::Always | VersionSpecifier::Never => [None, None], VersionSpecifier::From { from } => [Some(from), None], VersionSpecifier::Till { till } => [None, Some(till)], VersionSpecifier::Range { from, till } => [Some(from), Some(till)], diff --git a/crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.rs b/crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.rs index f7439dde26..24b8ff6fa9 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.rs +++ b/crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.stderr b/crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.stderr index d16f241cd4..d84fe9481d 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.stderr +++ b/crates/language-v2/tests/src/fail/p0_parsing/duplicate_map_key/test.stderr @@ -1,5 +1,5 @@ error: Duplicate map key. - --> src/fail/p0_parsing/duplicate_map_key/test.rs:20:25 + --> src/fail/p0_parsing/duplicate_map_key/test.rs:19:25 | -20 | field_1 = Required(Baz), +19 | field_1 = Required(Baz), | ^^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p0_parsing/duplicate_set_entry/test.rs b/crates/language-v2/tests/src/fail/p0_parsing/duplicate_set_entry/test.rs index 82381f0709..4446083096 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/duplicate_set_entry/test.rs +++ b/crates/language-v2/tests/src/fail/p0_parsing/duplicate_set_entry/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p0_parsing/duplicate_set_entry/test.stderr b/crates/language-v2/tests/src/fail/p0_parsing/duplicate_set_entry/test.stderr index ad6d460250..c681a771c5 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/duplicate_set_entry/test.stderr +++ b/crates/language-v2/tests/src/fail/p0_parsing/duplicate_set_entry/test.stderr @@ -1,5 +1,5 @@ error: Set entries must be unique. - --> src/fail/p0_parsing/duplicate_set_entry/test.rs:9:26 + --> src/fail/p0_parsing/duplicate_set_entry/test.rs:8:26 | -9 | versions = ["1.0.0", "1.0.0", "3.0.0"], +8 | versions = ["1.0.0", "1.0.0", "3.0.0"], | ^^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p0_parsing/empty_string/test.rs b/crates/language-v2/tests/src/fail/p0_parsing/empty_string/test.rs index 0208a33f65..4bfd0bde0f 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/empty_string/test.rs +++ b/crates/language-v2/tests/src/fail/p0_parsing/empty_string/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p0_parsing/empty_string/test.stderr b/crates/language-v2/tests/src/fail/p0_parsing/empty_string/test.stderr index f98c169e5e..ba8f45c279 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/empty_string/test.stderr +++ b/crates/language-v2/tests/src/fail/p0_parsing/empty_string/test.stderr @@ -1,5 +1,5 @@ error: Expected a non-empty string. - --> src/fail/p0_parsing/empty_string/test.rs:15:76 + --> src/fail/p0_parsing/empty_string/test.rs:14:76 | -15 | items = [Token(name = Bar, definitions = [TokenDefinition(Atom(""))])] +14 | items = [Token(name = Bar, definitions = [TokenDefinition(Atom(""))])] | ^^ diff --git a/crates/language-v2/tests/src/fail/p0_parsing/missing_field/test.rs b/crates/language-v2/tests/src/fail/p0_parsing/missing_field/test.rs index e4fb2a007a..26350ac22e 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/missing_field/test.rs +++ b/crates/language-v2/tests/src/fail/p0_parsing/missing_field/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p0_parsing/missing_field/test.stderr b/crates/language-v2/tests/src/fail/p0_parsing/missing_field/test.stderr index 1463fec03b..5d0f7019d5 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/missing_field/test.stderr +++ b/crates/language-v2/tests/src/fail/p0_parsing/missing_field/test.stderr @@ -1,5 +1,5 @@ error: Expected field: 'title'. - --> src/fail/p0_parsing/missing_field/test.rs:12:9 + --> src/fail/p0_parsing/missing_field/test.rs:11:9 | -12 | topics = [] +11 | topics = [] | ^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_field/test.rs b/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_field/test.rs index d198405a87..468308e3d7 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_field/test.rs +++ b/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_field/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_field/test.stderr b/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_field/test.stderr index 14207a6b03..8ce4541db9 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_field/test.stderr +++ b/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_field/test.stderr @@ -1,5 +1,5 @@ error: unexpected token, expected `)` - --> src/fail/p0_parsing/unrecognized_field/test.rs:19:13 + --> src/fail/p0_parsing/unrecognized_field/test.rs:18:13 | -19 | unrecognized_field = true +18 | unrecognized_field = true | ^^^^^^^^^^^^^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_variant/test.rs b/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_variant/test.rs index 2d2d29cdb1..89e9265400 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_variant/test.rs +++ b/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_variant/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar1, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_variant/test.stderr b/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_variant/test.stderr index f6170ae441..9aeffab85e 100644 --- a/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_variant/test.stderr +++ b/crates/language-v2/tests/src/fail/p0_parsing/unrecognized_variant/test.stderr @@ -1,5 +1,5 @@ error: Expected a variant: 'Struct' or 'Enum' or 'Repeated' or 'Separated' or 'Precedence' or 'Trivia' or 'Keyword' or 'Token' or 'Fragment' - --> src/fail/p0_parsing/unrecognized_variant/test.rs:24:26 + --> src/fail/p0_parsing/unrecognized_variant/test.rs:23:26 | -24 | items = [Unrecognized(true)] +23 | items = [Unrecognized(true)] | ^^^^^^^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_expression_name/test.rs b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_expression_name/test.rs index a10e9d5958..07308dd0d1 100644 --- a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_expression_name/test.rs +++ b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_expression_name/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_expression_name/test.stderr b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_expression_name/test.stderr index 0c2dd7a9fe..8a6046f192 100644 --- a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_expression_name/test.stderr +++ b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_expression_name/test.stderr @@ -1,5 +1,5 @@ error: An expression with the name 'Expression1' already exists. - --> src/fail/p1_definitions/duplicate_expression_name/test.rs:34:36 + --> src/fail/p1_definitions/duplicate_expression_name/test.rs:33:36 | -34 | ... name = Expression1, +33 | ... name = Expression1, | ^^^^^^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_item_name/test.rs b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_item_name/test.rs index aaa80240de..8112d88d1d 100644 --- a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_item_name/test.rs +++ b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_item_name/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar1, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_item_name/test.stderr b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_item_name/test.stderr index a7ef4133e9..134e4ccf98 100644 --- a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_item_name/test.stderr +++ b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_item_name/test.stderr @@ -1,5 +1,5 @@ error: An item with the name 'Bar1' already exists. - --> src/fail/p1_definitions/duplicate_item_name/test.rs:17:31 + --> src/fail/p1_definitions/duplicate_item_name/test.rs:16:31 | -17 | Struct(name = Bar1, fields = (field = Required(Bar2))), +16 | Struct(name = Bar1, fields = (field = Required(Bar2))), | ^^^^ diff --git a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_variant_name/test.rs b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_variant_name/test.rs index bdfd7b1bca..79a86a23cc 100644 --- a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_variant_name/test.rs +++ b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_variant_name/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_variant_name/test.stderr b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_variant_name/test.stderr index 22e7037c72..0b699861a1 100644 --- a/crates/language-v2/tests/src/fail/p1_definitions/duplicate_variant_name/test.stderr +++ b/crates/language-v2/tests/src/fail/p1_definitions/duplicate_variant_name/test.stderr @@ -1,5 +1,5 @@ error: A variant referencing 'Baz1' already exists. - --> src/fail/p1_definitions/duplicate_variant_name/test.rs:21:49 + --> src/fail/p1_definitions/duplicate_variant_name/test.rs:20:49 | -21 | EnumVariant(reference = Baz1) +20 | EnumVariant(reference = Baz1) | ^^^^ diff --git a/crates/language-v2/tests/src/fail/p1_definitions/operator_mismatch/test.rs b/crates/language-v2/tests/src/fail/p1_definitions/operator_mismatch/test.rs index da887e4285..acc24bcc88 100644 --- a/crates/language-v2/tests/src/fail/p1_definitions/operator_mismatch/test.rs +++ b/crates/language-v2/tests/src/fail/p1_definitions/operator_mismatch/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p1_definitions/operator_mismatch/test.stderr b/crates/language-v2/tests/src/fail/p1_definitions/operator_mismatch/test.stderr index b24c5f0c1e..dc341c7686 100644 --- a/crates/language-v2/tests/src/fail/p1_definitions/operator_mismatch/test.stderr +++ b/crates/language-v2/tests/src/fail/p1_definitions/operator_mismatch/test.stderr @@ -1,5 +1,5 @@ error: All operators under the same expression must have the same model and type. - --> src/fail/p1_definitions/operator_mismatch/test.rs:19:32 + --> src/fail/p1_definitions/operator_mismatch/test.rs:18:32 | -19 | name = Foo, +18 | name = Foo, | ^^^ diff --git a/crates/language-v2/tests/src/fail/p2_version_specifiers/unordered_version_pair/test.rs b/crates/language-v2/tests/src/fail/p2_version_specifiers/unordered_version_pair/test.rs index 3194b1fa2d..8c5aa7caec 100644 --- a/crates/language-v2/tests/src/fail/p2_version_specifiers/unordered_version_pair/test.rs +++ b/crates/language-v2/tests/src/fail/p2_version_specifiers/unordered_version_pair/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = One, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p2_version_specifiers/unordered_version_pair/test.stderr b/crates/language-v2/tests/src/fail/p2_version_specifiers/unordered_version_pair/test.stderr index 786f85a5dd..0687ee48c6 100644 --- a/crates/language-v2/tests/src/fail/p2_version_specifiers/unordered_version_pair/test.stderr +++ b/crates/language-v2/tests/src/fail/p2_version_specifiers/unordered_version_pair/test.stderr @@ -1,5 +1,5 @@ error: Version '3.0.0' must be less than corresponding version '2.0.0'. - --> src/fail/p2_version_specifiers/unordered_version_pair/test.rs:21:52 + --> src/fail/p2_version_specifiers/unordered_version_pair/test.rs:20:52 | -21 | ... enabled = Range(from = "3.0.0", till = "2.0.0") +20 | ... enabled = Range(from = "3.0.0", till = "2.0.0") | ^^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p2_version_specifiers/version_not_found/test.rs b/crates/language-v2/tests/src/fail/p2_version_specifiers/version_not_found/test.rs index d7a80290de..657a3e7ace 100644 --- a/crates/language-v2/tests/src/fail/p2_version_specifiers/version_not_found/test.rs +++ b/crates/language-v2/tests/src/fail/p2_version_specifiers/version_not_found/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = One, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p2_version_specifiers/version_not_found/test.stderr b/crates/language-v2/tests/src/fail/p2_version_specifiers/version_not_found/test.stderr index b043a98012..019ba94b81 100644 --- a/crates/language-v2/tests/src/fail/p2_version_specifiers/version_not_found/test.stderr +++ b/crates/language-v2/tests/src/fail/p2_version_specifiers/version_not_found/test.stderr @@ -1,5 +1,5 @@ error: Version '3.0.0' does not exist in the language definition. - --> src/fail/p2_version_specifiers/version_not_found/test.rs:19:76 + --> src/fail/p2_version_specifiers/version_not_found/test.rs:18:76 | -19 | field_1 = Optional(reference = Two, enabled = From("3.0.0")), +18 | field_1 = Optional(reference = Two, enabled = From("3.0.0")), | ^^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p3_references/disabled_too_late/test.rs b/crates/language-v2/tests/src/fail/p3_references/disabled_too_late/test.rs index c10b0e90e8..dbc52f9274 100644 --- a/crates/language-v2/tests/src/fail/p3_references/disabled_too_late/test.rs +++ b/crates/language-v2/tests/src/fail/p3_references/disabled_too_late/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = One, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p3_references/disabled_too_late/test.stderr b/crates/language-v2/tests/src/fail/p3_references/disabled_too_late/test.stderr index ef369807c6..69a44c997a 100644 --- a/crates/language-v2/tests/src/fail/p3_references/disabled_too_late/test.stderr +++ b/crates/language-v2/tests/src/fail/p3_references/disabled_too_late/test.stderr @@ -1,5 +1,5 @@ error: Parent scope is only enabled in '2.0.0..3.0.0'. - --> src/fail/p3_references/disabled_too_late/test.rs:29:79 + --> src/fail/p3_references/disabled_too_late/test.rs:28:79 | -29 | fields = (field_1 = Optional(reference = Three, enabled = Till("4.0.0"))) +28 | fields = (field_1 = Optional(reference = Three, enabled = Till("4.0.0"))) | ^^^^ diff --git a/crates/language-v2/tests/src/fail/p3_references/enabled_too_early/test.rs b/crates/language-v2/tests/src/fail/p3_references/enabled_too_early/test.rs index 60913a042b..b3cbaae7c6 100644 --- a/crates/language-v2/tests/src/fail/p3_references/enabled_too_early/test.rs +++ b/crates/language-v2/tests/src/fail/p3_references/enabled_too_early/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = One, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p3_references/enabled_too_early/test.stderr b/crates/language-v2/tests/src/fail/p3_references/enabled_too_early/test.stderr index e1e7c3e8b2..598d0b45b3 100644 --- a/crates/language-v2/tests/src/fail/p3_references/enabled_too_early/test.stderr +++ b/crates/language-v2/tests/src/fail/p3_references/enabled_too_early/test.stderr @@ -1,5 +1,5 @@ error: Parent scope is only enabled in '2.0.0..3.0.0'. - --> src/fail/p3_references/enabled_too_early/test.rs:29:79 + --> src/fail/p3_references/enabled_too_early/test.rs:28:79 | -29 | fields = (field_1 = Optional(reference = Three, enabled = From("1.0.0"))) +28 | fields = (field_1 = Optional(reference = Three, enabled = From("1.0.0"))) | ^^^^ diff --git a/crates/language-v2/tests/src/fail/p3_references/invalid_filter/test.rs b/crates/language-v2/tests/src/fail/p3_references/invalid_filter/test.rs index 44b9c77cc3..2d56fc5a05 100644 --- a/crates/language-v2/tests/src/fail/p3_references/invalid_filter/test.rs +++ b/crates/language-v2/tests/src/fail/p3_references/invalid_filter/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p3_references/invalid_filter/test.stderr b/crates/language-v2/tests/src/fail/p3_references/invalid_filter/test.stderr index c567bd099a..3fbec1ce68 100644 --- a/crates/language-v2/tests/src/fail/p3_references/invalid_filter/test.stderr +++ b/crates/language-v2/tests/src/fail/p3_references/invalid_filter/test.stderr @@ -1,5 +1,5 @@ error: Reference 'Baz' of kind 'Fragment' is not valid. Expected: [Struct, Enum, Repeated, Separated, Precedence, Keyword, Token] - --> src/fail/p3_references/invalid_filter/test.rs:16:63 + --> src/fail/p3_references/invalid_filter/test.rs:15:63 | -16 | Struct(name = Bar, fields = (field = Required(Baz))), +15 | Struct(name = Bar, fields = (field = Required(Baz))), | ^^^ diff --git a/crates/language-v2/tests/src/fail/p3_references/invalid_version/test.rs b/crates/language-v2/tests/src/fail/p3_references/invalid_version/test.rs index 70783ad281..6844c399dc 100644 --- a/crates/language-v2/tests/src/fail/p3_references/invalid_version/test.rs +++ b/crates/language-v2/tests/src/fail/p3_references/invalid_version/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p3_references/invalid_version/test.stderr b/crates/language-v2/tests/src/fail/p3_references/invalid_version/test.stderr index 4a843928ed..e4aa8b169c 100644 --- a/crates/language-v2/tests/src/fail/p3_references/invalid_version/test.stderr +++ b/crates/language-v2/tests/src/fail/p3_references/invalid_version/test.stderr @@ -1,17 +1,17 @@ error: Reference 'Baz' is only defined in '2.0.0..3.0.0', but not in '3.0.0..MAX'. - --> src/fail/p3_references/invalid_version/test.rs:21:41 + --> src/fail/p3_references/invalid_version/test.rs:20:41 | -21 | ... reference = Baz, +20 | ... reference = Baz, | ^^^ error: Reference 'Baz' is only defined in '2.0.0..3.0.0', but not in '1.0.0..2.0.0'. - --> src/fail/p3_references/invalid_version/test.rs:26:41 + --> src/fail/p3_references/invalid_version/test.rs:25:41 | -26 | ... reference = Baz, +25 | ... reference = Baz, | ^^^ error: Reference 'Baz' is only defined in '2.0.0..3.0.0', but not in '1.0.0..2.0.0 | 3.0.0..MAX'. - --> src/fail/p3_references/invalid_version/test.rs:31:41 + --> src/fail/p3_references/invalid_version/test.rs:30:41 | -31 | ... reference = Baz +30 | ... reference = Baz | ^^^ diff --git a/crates/language-v2/tests/src/fail/p3_references/unknown_reference/test.rs b/crates/language-v2/tests/src/fail/p3_references/unknown_reference/test.rs index 9ddd77fff9..9b63b2399f 100644 --- a/crates/language-v2/tests/src/fail/p3_references/unknown_reference/test.rs +++ b/crates/language-v2/tests/src/fail/p3_references/unknown_reference/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p3_references/unknown_reference/test.stderr b/crates/language-v2/tests/src/fail/p3_references/unknown_reference/test.stderr index f7347494d8..8fbff7ca72 100644 --- a/crates/language-v2/tests/src/fail/p3_references/unknown_reference/test.stderr +++ b/crates/language-v2/tests/src/fail/p3_references/unknown_reference/test.stderr @@ -1,5 +1,5 @@ error: Reference to unknown item 'Unknown'. - --> src/fail/p3_references/unknown_reference/test.rs:18:75 + --> src/fail/p3_references/unknown_reference/test.rs:17:75 | -18 | fields = (field_1 = Required(Baz), field_2 = Required(Unknown)) +17 | fields = (field_1 = Required(Baz), field_2 = Required(Unknown)) | ^^^^^^^ diff --git a/crates/language-v2/tests/src/fail/p4_unreachabe_items/unreachable/test.rs b/crates/language-v2/tests/src/fail/p4_unreachabe_items/unreachable/test.rs index 93291f97ef..04a3c1fc93 100644 --- a/crates/language-v2/tests/src/fail/p4_unreachabe_items/unreachable/test.rs +++ b/crates/language-v2/tests/src/fail/p4_unreachabe_items/unreachable/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar1, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p4_unreachabe_items/unreachable/test.stderr b/crates/language-v2/tests/src/fail/p4_unreachabe_items/unreachable/test.stderr index 7abce86300..711a76be24 100644 --- a/crates/language-v2/tests/src/fail/p4_unreachabe_items/unreachable/test.stderr +++ b/crates/language-v2/tests/src/fail/p4_unreachabe_items/unreachable/test.stderr @@ -1,11 +1,11 @@ error: Item 'Baz1' is not reachable from grammar root. - --> src/fail/p4_unreachabe_items/unreachable/test.rs:20:31 + --> src/fail/p4_unreachabe_items/unreachable/test.rs:19:31 | -20 | Struct(name = Baz1, fields = (field = Required(Baz2))), +19 | Struct(name = Baz1, fields = (field = Required(Baz2))), | ^^^^ error: Item 'Baz2' is not reachable from grammar root. - --> src/fail/p4_unreachabe_items/unreachable/test.rs:21:31 + --> src/fail/p4_unreachabe_items/unreachable/test.rs:20:31 | -21 | Struct(name = Baz2, fields = (field = Required(Baz1))) +20 | Struct(name = Baz2, fields = (field = Required(Baz1))) | ^^^^ diff --git a/crates/language-v2/tests/src/fail/p5_unused_versions/unused_version/test.rs b/crates/language-v2/tests/src/fail/p5_unused_versions/unused_version/test.rs index a5aedf6de6..d6aeb1b2cc 100644 --- a/crates/language-v2/tests/src/fail/p5_unused_versions/unused_version/test.rs +++ b/crates/language-v2/tests/src/fail/p5_unused_versions/unused_version/test.rs @@ -2,7 +2,6 @@ language_v2_macros::compile!(Language( name = Foo, - binding_rules_file = "bindings/rules.msgb", root_item = Bar, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), diff --git a/crates/language-v2/tests/src/fail/p5_unused_versions/unused_version/test.stderr b/crates/language-v2/tests/src/fail/p5_unused_versions/unused_version/test.stderr index d514c232c4..a2e8095c09 100644 --- a/crates/language-v2/tests/src/fail/p5_unused_versions/unused_version/test.stderr +++ b/crates/language-v2/tests/src/fail/p5_unused_versions/unused_version/test.stderr @@ -1,5 +1,5 @@ error: Item 'Baz' is not used in versions: 1.0.0..2.0.0 - --> src/fail/p5_unused_versions/unused_version/test.rs:20:30 + --> src/fail/p5_unused_versions/unused_version/test.rs:19:30 | -20 | Token(name = Baz, definitions = [TokenDefinition(Atom("baz"))]) +19 | Token(name = Baz, definitions = [TokenDefinition(Atom("baz"))]) | ^^^ diff --git a/crates/language-v2/tests/src/pass/tiny_language.rs b/crates/language-v2/tests/src/pass/tiny_language.rs index 1f23e1d028..33924e1d2e 100644 --- a/crates/language-v2/tests/src/pass/tiny_language.rs +++ b/crates/language-v2/tests/src/pass/tiny_language.rs @@ -6,7 +6,6 @@ use semver::Version; language_v2_macros::compile!(Language( name = Tiny, - binding_rules_file = "tiny/bindings/rules.msgb", root_item = Foo, leading_trivia = Sequence([]), trailing_trivia = Sequence([]), @@ -39,7 +38,6 @@ fn definition() { tiny::TinyDefinition::create(), Language { name: "Tiny".into(), - binding_rules_file: "tiny/bindings/rules.msgb".into(), root_item: "Foo".into(), leading_trivia: TriviaParser::Sequence { parsers: [].into() }, trailing_trivia: TriviaParser::Sequence { parsers: [].into() }, diff --git a/crates/solidity-v2/inputs/language/bindings/rules.msgb b/crates/solidity-v2/inputs/language/bindings/rules.msgb deleted file mode 100644 index a9cb2cc873..0000000000 --- a/crates/solidity-v2/inputs/language/bindings/rules.msgb +++ /dev/null @@ -1,3407 +0,0 @@ -global ROOT_NODE -global FILE_PATH -global JUMP_TO_SCOPE_NODE - -attribute node_definition = node => type = "pop_symbol", node_symbol = node, is_definition -attribute node_reference = node => type = "push_symbol", node_symbol = node, is_reference -attribute node_symbol = node => symbol = (source-text node), source_node = node -attribute pop_symbol = symbol => type = "pop_symbol", symbol = symbol -attribute push_symbol = symbol => type = "push_symbol", symbol = symbol -attribute symbol_definition = symbol => type = "pop_symbol", symbol = symbol, is_definition -attribute symbol_reference = symbol => type = "push_symbol", symbol = symbol, is_reference - -attribute scoped_node_definition = node => type = "pop_scoped_symbol", node_symbol = node, is_definition -attribute scoped_node_reference = node => type = "push_scoped_symbol", node_symbol = node, is_reference -attribute pop_scoped_symbol = symbol => type = "pop_scoped_symbol", symbol = symbol -attribute push_scoped_symbol = symbol => type = "push_scoped_symbol", symbol = symbol - -;; Keeps a link to the enclosing definition (eg. contract, library or global -;; function) to provide extension scopes. -inherit .enclosing_def - -inherit .parent_scope -inherit .lexical_scope - -; Used to resolve extension methods for `using for *` directives -; This is used as a minor optimization to avoid introducing new possible paths -; when there are no `using for *` directives in the contract. -inherit .star_extension - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Source unit (aka .sol file) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@source_unit [SourceUnit] { - ;; All lexical_scope nodes eventually connect to the file's root scope - node @source_unit.lexical_scope - - ;; This provides all the exported symbols from the file - node @source_unit.defs - - ;; Connect to ROOT_NODE to export our symbols - node export - edge ROOT_NODE -> export - edge export -> @source_unit.defs - - ; This is a user file, so we want to export under the file's path symbol - attr (export) pop_symbol = FILE_PATH - - ; ... and also import the global built-ins - node built_ins - ; __SLANG_SOLIDITY_BUILT_INS_SCOPE_GUARD__ keep in sync with built-ins loading code - attr (built_ins) push_symbol = "@@built-ins@@" - - edge @source_unit.lexical_scope -> built_ins - edge built_ins -> ROOT_NODE - - let @source_unit.enclosing_def = #null - - ;; This defines a parent_scope at the source unit level (this attribute is - ;; inherited) for contracts to resolve bases (both in inheritance lists and - ;; override specifiers) - let @source_unit.parent_scope = @source_unit.lexical_scope - - ; This is used to indicate the resolution algorithm that here's where it - ; should inject any possible extension scopes - attr (@source_unit.lexical_scope) extension_hook - attr (@source_unit.lexical_scope) is_endpoint - - ; Provide a default star extension sink node that gets inherited. This is - ; connected to from expressions, and those can potentially happen anywhere. - node @source_unit.star_extension - - ; We'll collect global using extensions in this scope, which we can hook from - ; contracts and global functions - node @source_unit.extensions - attr (@source_unit.extensions) is_exported -} - -;; Top-level definitions... -@source_unit [SourceUnit [SourceUnitMembers - [SourceUnitMember @unit_member ( - [ContractDefinition] - | [LibraryDefinition] - | [InterfaceDefinition] - | [StructDefinition] - | [EnumDefinition] - | [FunctionDefinition] - | [ConstantDefinition] - | [ErrorDefinition] - | [UserDefinedValueTypeDefinition] - | [EventDefinition] - )] -]] { - edge @source_unit.lexical_scope -> @unit_member.def - attr (@source_unit.lexical_scope -> @unit_member.def) precedence = 1 - - edge @source_unit.defs -> @unit_member.def - - ; In the general case, the lexical scope of the definition connects directly - ; to the source unit's - edge @unit_member.lexical_scope -> @source_unit.lexical_scope -} - -@source_unit [SourceUnit [SourceUnitMembers [SourceUnitMember @using [UsingDirective]]]] { - ; Link the using directive to the source unit extensions scope, which will - ; then be hooked to the lexical_scope via the enclosing def (contract, - ; library, etc.) extension_scope - edge @source_unit.extensions -> @using.def -} - -@source_unit [SourceUnit [SourceUnitMembers - [SourceUnitMember @unit_member ( - [ContractDefinition] - | [LibraryDefinition] - )] -]] { - ;; Global using extensions also apply to contracts and libraries - edge @unit_member.extensions -> @source_unit.extensions -} - -@source_unit [SourceUnit [SourceUnitMembers [SourceUnitMember @function [FunctionDefinition]]]] { - ;; Global functions act as enclosing definitions for expressions within and - ;; use the source unit's extension scope - attr (@function.def) extension_scope = @source_unit.extensions - let @function.enclosing_def = @function.def -} - -@source_unit [SourceUnit [SourceUnitMembers [SourceUnitMember - @using [UsingDirective [GlobalKeyword]] -]]] { - ; global using directives are exported by this source unit - edge @source_unit.defs -> @using.def - ; and should be directly accesible from the file's lexical scope - edge @source_unit.lexical_scope -> @using.def -} - -;; Import connections to the source unit -@source_unit [SourceUnit [SourceUnitMembers - [SourceUnitMember [ImportDirective - [ImportClause @import ( - [PathImport] - | [NamedImport] - | [ImportDeconstruction] - )] - ]] -]] { - node @import.defs - edge @source_unit.defs -> @import.defs - edge @source_unit.lexical_scope -> @import.defs -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Imports -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -[ImportClause - [_ - path: [StringLiteral - @path ([DoubleQuotedStringLiteral] | [SingleQuotedStringLiteral]) - ] - ] -] { - ;; This node represents the imported file and the @path.import node is used by - ;; all subsequent import rules - node @path.import - - let resolved_path = (resolve-path FILE_PATH @path) - attr (@path.import) push_symbol = resolved_path - - edge @path.import -> ROOT_NODE -} - -;;; `import ` -@import [PathImport - path: [StringLiteral - @path ([DoubleQuotedStringLiteral] | [SingleQuotedStringLiteral]) - ] -] { - ;; This is the "lexical" connection, which makes all symbols exported from the - ;; imported source unit available for resolution globally at this' source unit - ;; scope - edge @import.defs -> @path.import -} - -;;; `import as ` -@import [PathImport - path: [StringLiteral - @path ([DoubleQuotedStringLiteral] | [SingleQuotedStringLiteral]) - ] - alias: [ImportAlias @alias [Identifier]] -] { - node def - attr (def) node_definition = @alias - attr (def) definiens_node = @import - edge @import.defs -> def - - node member - attr (member) pop_symbol = "." - edge def -> member - - ;; Lexical connection, which makes the import available as a member through - ;; the alias identifier - edge member -> @path.import -} - -;;; `import * as from ` -@import [NamedImport - alias: [ImportAlias @alias [Identifier]] - path: [StringLiteral - @path ([DoubleQuotedStringLiteral] | [SingleQuotedStringLiteral]) - ] -] { - node def - attr (def) node_definition = @alias - attr (def) definiens_node = @import - edge @import.defs -> def - - node member - attr (member) pop_symbol = "." - edge def -> member - - ;; Lexical connection, which makes the import available as a member through - ;; the alias identifier - edge member -> @path.import -} - -;;; `import { [as ] ...} from ` -@import [ImportDeconstruction path: [StringLiteral - @path ([DoubleQuotedStringLiteral] | [SingleQuotedStringLiteral]) -]] { - node @import.yul_defs - node @import.yul_path - - ;; Each imported symbol will have an alternative path to make symbols - ;; accessible in assembly blocks - node pop_yul - attr (pop_yul) pop_symbol = "@yul" - node push_yul - attr (push_yul) push_symbol = "@yul" - - edge @import.defs -> pop_yul - edge pop_yul -> @import.yul_defs - edge @import.yul_path -> push_yul - edge push_yul -> @path.import -} - -@import [ImportDeconstruction - symbols: [ImportDeconstructionSymbols @symbol [ImportDeconstructionSymbol]] - path: [StringLiteral - @path ([DoubleQuotedStringLiteral] | [SingleQuotedStringLiteral]) - ] -] { - ;; We define these intermediate nodes for convenience only, to make the - ;; queries simpler in the two rules below - node @symbol.def - edge @import.defs -> @symbol.def - - node @symbol.import - edge @symbol.import -> @path.import - - edge @import.yul_defs -> @symbol.def - edge @symbol.import -> @import.yul_path -} - -@symbol [ImportDeconstructionSymbol @name name: [Identifier] .] { - attr (@symbol.def) node_definition = @name - attr (@symbol.def) definiens_node = @symbol - attr (@symbol.import) node_reference = @name - edge @symbol.def -> @symbol.import -} - -@symbol [ImportDeconstructionSymbol - @name name: [Identifier] - alias: [ImportAlias @alias [Identifier]] -] { - attr (@symbol.def) node_definition = @alias - attr (@symbol.def) definiens_node = @symbol - attr (@symbol.import) node_reference = @name - edge @symbol.def -> @symbol.import -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Common inheritance rules (apply to contracts and interfaces) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@specifier [InheritanceSpecifier [InheritanceTypes - [InheritanceType @type_name [IdentifierPath]] -]] { - ;; This should point to the enclosing contract or interface definition - let heir = @specifier.heir - - ;; Resolve base names through the parent scope of our heir (contract or - ;; interface), aka the source unit - edge @type_name.push_end -> heir.parent_scope - - ; Access instance members of the inherited contract/interface, from the - ; instance scope of the inheriting contract/interface - node instance - attr (instance) push_symbol = "@instance" - edge heir.instance -> instance - edge instance -> @type_name.push_begin - - ; Base members can also be accessed (from the instance scope) qualified with - ; the base name (eg. `Base.something`) - node member_pop - attr (member_pop) pop_symbol = "." - edge heir.instance -> @type_name.pop_begin - edge @type_name.pop_end -> member_pop - edge member_pop -> instance - - ; Base namespace-like members (ie. enums, structs, etc) are also accessible as - ; our own namespace members - node ns_member - attr (ns_member) push_symbol = "." - edge heir.ns -> ns_member - edge ns_member -> @type_name.push_begin -} - -;; The next couple of rules setup a `.parent_refs` attribute to use in the -;; resolution algorithm to perform linearisation of a contract hierarchy. - -;; NOTE: we use anchors here to prevent the query engine from returning all the -;; sublists of possible parents -@specifier [InheritanceSpecifier [InheritanceTypes . @parents [_]+ .]] { - var parent_refs = [] - for parent in @parents { - if (eq (node-type parent) "InheritanceType") { - ;; this is intentionally reversed because of how Solidity linearised the contract bases - set parent_refs = (concat [parent.ref] parent_refs) - } - } - let @specifier.parent_refs = parent_refs -} - -@parent [InheritanceType @type_name [IdentifierPath]] { - let @parent.ref = @type_name.push_begin -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Contracts -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@contract [ContractDefinition @name name: [Identifier]] { - node @contract.lexical_scope - node @contract.extensions - node @contract.def - node @contract.members - node @contract.ns - node @contract.modifiers - node @contract.instance - - attr (@contract.def) node_definition = @name - attr (@contract.def) definiens_node = @contract - ; The .extensions node is where `using` directives will hook the definitions - attr (@contract.def) extension_scope = @contract.extensions - attr (@contract.extensions) is_exported - - edge @contract.lexical_scope -> @contract.instance - attr (@contract.lexical_scope -> @contract.instance) precedence = 1 - - ; Instance scope can also see members and our namespace definitions - edge @contract.instance -> @contract.members - edge @contract.instance -> @contract.ns - - let @contract.enclosing_def = @contract.def - - ;; External "instance" scope access: either member access through a variable - ;; of the contract's type, or through calling (which happens on `new` - ;; invocations or casting). These should access only externally accessible - ;; members, such as functions and public variables. - node member - attr (member) pop_symbol = "." - edge member -> @contract.instance - - node type_def - attr (type_def) pop_symbol = "@typeof" - edge @contract.def -> type_def - edge type_def -> member - - node call - attr (call) pop_symbol = "()" - edge @contract.def -> call - edge call -> member - - ;; "namespace" scope access - node ns_member - attr (ns_member) pop_symbol = "." - edge @contract.def -> ns_member - edge ns_member -> @contract.ns - - ; Finally there's an @instance guarded path used by derived contracts to - ; access instance accessible members - node instance - attr (instance) pop_symbol = "@instance" - edge @contract.def -> instance - edge instance -> @contract.instance - - ; "this" keyword is available in our lexical scope and can access any - ; externally available member - node this - attr (this) pop_symbol = "this" - edge @contract.lexical_scope -> this - edge this -> member - - ;; Modifiers are available as a contract type members through a special '@modifier' guard - node modifier - attr (modifier) pop_symbol = "@modifier" - edge @contract.ns -> modifier - edge modifier -> @contract.modifiers - - ;; Qualified access to members of the contract itself (eg. `Foo.x` in a function body in `Foo`) - node self - attr (self) pop_symbol = (source-text @name) - edge @contract.lexical_scope -> self - edge self -> member - - ; There may be attached functions to our type. For the general case of - ; variables of our type, that's already handled via normal lexical scope - ; resolution. But for casting/`new` invocations that we resolve through the - ; `()` guard above, we need to explicitly jump to the extension scope from - ; here to attempt resolving the attached function. We cannot jump back to the - ; parent scope because that would create a cycle in the graph. - node push_typeof - attr (push_typeof) push_symbol = "@typeof" - node push_name - attr (push_name) push_symbol = (source-text @name) - node hook - attr (hook) extension_hook - attr (hook) is_endpoint - - edge call -> push_typeof - edge push_typeof -> push_name - edge push_name -> hook - - if (version-matches "< 0.5.0") { - ; For Solidity < 0.5.0 `this` also acts like an `address` - node address_typeof - attr (address_typeof) push_symbol = "@typeof" - node address_ref - attr (address_ref) push_symbol = "address" - edge this -> address_typeof - edge address_typeof -> address_ref - edge address_ref -> @contract.lexical_scope - - ; Instances of the contract can also be used as addresses - node member_guard_pop - attr (member_guard_pop) pop_symbol = "@typeof" - - edge @contract.def -> member_guard_pop - edge member_guard_pop -> address_typeof - } - - ; This is the connection point to resolve attached functions by `using for *` - node @contract.star_extension - attr (@contract.star_extension) push_symbol = "@*" - - if (version-matches "< 0.7.0") { - ; For Solidity < 0.7.0 using directives are inherited, so we need to connect - ; always For newer versions, this connection only happens when there is a - ; `using for *` directive in the contract (see rule below) - edge @contract.star_extension -> @contract.lexical_scope - } - - ; Path to resolve the built-in type for type() expressions - node type - attr (type) pop_symbol = "@type" - node type_contract_type - attr (type_contract_type) push_symbol = "%ContractTypeType" - edge @contract.def -> type - edge type -> type_contract_type - edge type_contract_type -> @contract.parent_scope - - ;; This scope provides access to state variables from Yul assembly blocks - node @contract.yul - attr (@contract.yul) pop_symbol = "@yul" - edge @contract.lexical_scope -> @contract.yul - ;; Also to state variables (and constants) from base contracts - edge @contract.instance -> @contract.yul -} - -@contract [ContractDefinition - [ContractSpecifiers [ContractSpecifier @specifier [InheritanceSpecifier]]] -] { - ; The `.heir` scoped variable allows the rules for `InheritanceSpecifier` - ; above to connect the instance scope of this contract to the parents. - let @specifier.heir = @contract - attr (@contract.def) parents = @specifier.parent_refs - if (version-matches "< 0.7.0") { - attr (@contract.def) inherit_extensions - } - - ; The rest of these statements deal with defining and connecting the `super` - ; keyword path. - - ; `super_scope` is where we hook all references to our parent contracts - node @contract.super_scope - - ; Define "super" in the lexical scope - node @contract.super - attr (@contract.super) pop_symbol = "super" - edge @contract.lexical_scope -> @contract.super - - node @contract.super_dot - attr (@contract.super_dot) pop_symbol = "." - edge @contract.super -> @contract.super_dot - - ; Then connect it through an `@instance` guard to the parent contracts through - ; `super_scope`. This allows "instance"-like access to members of parents - ; through `super`. - node super_instance - attr (super_instance) push_symbol = "@instance" - edge @contract.super_dot -> super_instance - edge super_instance -> @contract.super_scope -} - -@contract [ContractDefinition - [ContractSpecifiers [ContractSpecifier [InheritanceSpecifier - [InheritanceTypes [InheritanceType @type_name [IdentifierPath]]] - ]]] -] { - ;; The base contract defs are directly accesible through our super scope - edge @contract.super_scope -> @type_name.push_begin -} - -@contract [ContractDefinition - [ContractSpecifiers [ContractSpecifier [InheritanceSpecifier - [InheritanceTypes [InheritanceType @args [ArgumentsDeclaration]]] - ]]] -] { - ;; Resolve arguments in inheritance specifiers using the parent scope - edge @args.lexical_scope -> @contract.parent_scope -} - -@contract [ContractDefinition - [ContractSpecifiers [ContractSpecifier - [StorageLayoutSpecifier @expr [Expression]] - ]] -] { - ;; Resolve expressions in storage layout specifiers using the parent scope - let @expr.lexical_scope = @contract.parent_scope -} - -; Pure definitions that cannot contain expressions -@contract [ContractDefinition [ContractMembers - [ContractMember @member ( - [EnumDefinition] - | [StructDefinition] - | [EventDefinition] - | [ErrorDefinition] - | [UserDefinedValueTypeDefinition] - )] -]] { - edge @member.lexical_scope -> @contract.lexical_scope -} - -; Definitions that can contain expressions need two scopes: -; - normal lexical scope for resolving types -; - extended scope (extended by using directives) for resolving expressions -@contract [ContractDefinition [ContractMembers - [ContractMember @member ( - [FunctionDefinition] - | [ConstructorDefinition] - | [ModifierDefinition] - | [FallbackFunctionDefinition] - | [ReceiveFunctionDefinition] - | [UnnamedFunctionDefinition] - | [StateVariableDefinition] - )] -]] { - edge @member.lexical_scope -> @contract.lexical_scope -} - -@contract [ContractDefinition [ContractMembers - [ContractMember @using [UsingDirective]] -]] { - ; Hook the using definition in the extensions scope - edge @contract.extensions -> @using.def -} - -@contract [ContractDefinition [ContractMembers - [ContractMember @member ( - [EnumDefinition] - | [StructDefinition] - | [EventDefinition] - | [ErrorDefinition] - | [UserDefinedValueTypeDefinition] - )] -]] { - ; These definition go into the "namespace" scope and are accessible externally - ; via qualified naming (eg. `Contract.MyStruct`) - edge @contract.ns -> @member.def -} - -@contract [ContractDefinition [ContractMembers - [ContractMember @state_var [StateVariableDefinition]] -]] { - ; State variables are available to derived contracts. - edge @contract.instance -> @state_var.def - - ; State variables should also be available in Yul assembly blocks - edge @contract.yul -> @state_var.def -} - -;; Public state variables are also exposed as external member functions -@contract [ContractDefinition [ContractMembers - [ContractMember @state_var [StateVariableDefinition - [StateVariableAttributes [StateVariableAttribute [PublicKeyword]]] - ]] -]] { - edge @contract.members -> @state_var.def -} - -@contract [ContractDefinition [ContractMembers - [ContractMember @function [FunctionDefinition]] -]] { - ;; Contract functions are also accessible for an instance of the contract - edge @contract.members -> @function.def - - ;; This may prioritize this definition (when there are multiple options) - ;; according to the C3 linerisation ordering - attr (@function.def) parents = [@contract.def] -} - -@contract [ContractDefinition [ContractMembers - [ContractMember @function [FunctionDefinition - [FunctionAttributes [FunctionAttribute ([ExternalKeyword] | [PublicKeyword])]] - ]] -]] { - ; Public or external functions are also accessible through the contract type - ; (to retrieve their `.selector` for example) - edge @contract.ns -> @function.def -} - -@contract [ContractDefinition members: [ContractMembers - [ContractMember @modifier [ModifierDefinition]] -]] { - ; Modifiers live in their own special scope - edge @contract.modifiers -> @modifier.def - - ;; This may prioritize this definition (when there are multiple options) - ;; according to the C3 linerisation ordering - attr (@modifier.def) parents = [@contract.def] -} - -@contract [ContractDefinition [ContractMembers [ContractMember - [UsingDirective [UsingTarget [Asterisk]]] -]]] { - ; Connect the star extension node to the resolution extended scope if there is - ; a `using for *` directive in the contract - edge @contract.star_extension -> @contract.lexical_scope -} - -; This applies to both state variables and function definitions -@override [OverrideSpecifier [OverridePathsDeclaration [OverridePaths - @base_ident [IdentifierPath] -]]] { - ;; Resolve overriden bases when listed in the function or modifiers modifiers - edge @base_ident.push_end -> @override.parent_scope -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Interfaces -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@interface [InterfaceDefinition @name name: [Identifier]] { - node @interface.lexical_scope - node @interface.def - node @interface.members - node @interface.ns - node @interface.instance - - attr (@interface.def) node_definition = @name - attr (@interface.def) definiens_node = @interface - - edge @interface.lexical_scope -> @interface.instance - - ; The extensions node is required for the inheritance rules, but not used in interfaces - let @interface.extensions = (node) - - edge @interface.instance -> @interface.members - edge @interface.instance -> @interface.ns - - ;; External "instance" like access path, to access members of a variable of - ;; the interface's type or through a casting call. - node member - attr (member) pop_symbol = "." - edge member -> @interface.instance - - node typeof - attr (typeof) pop_symbol = "@typeof" - edge @interface.def -> typeof - edge typeof -> member - - node call - attr (call) pop_symbol = "()" - edge @interface.def -> call - edge call -> member - - ; From a call we may need to resolve using the extensions scope, in case there's - ; a `using` directive on our type. This path ends up jumping to scope just to - ; handle that case. - node push_typeof - attr (push_typeof) push_symbol = "@typeof" - node push_name - attr (push_name) push_symbol = (source-text @name) - edge call -> push_typeof - edge push_typeof -> push_name - node hook - attr (hook) extension_hook - attr (hook) is_endpoint - edge push_name -> hook - - ;; "namespace" like access path - node ns_member - attr (ns_member) pop_symbol = "." - edge @interface.def -> ns_member - edge ns_member -> @interface.ns - - ; Finally there's guarded `@instance` path used by derived contracts to access - ; instance accessible members - node instance - attr (instance) pop_symbol = "@instance" - edge @interface.def -> instance - edge instance -> @interface.instance - - ; Path to resolve the built-in type for type() expressions - node type - attr (type) pop_symbol = "@type" - node type_interface_type - attr (type_interface_type) push_symbol = "%InterfaceTypeType" - edge @interface.def -> type - edge type -> type_interface_type - edge type_interface_type -> @interface.parent_scope - - if (version-matches "< 0.5.0") { - ; For Solidity < 0.5.0 instance can also be used as addresses - node member_guard_pop - attr (member_guard_pop) pop_symbol = "@typeof" - node address_typeof - attr (address_typeof) push_symbol = "@typeof" - node address_ref - attr (address_ref) push_symbol = "address" - - edge @interface.def -> member_guard_pop - edge member_guard_pop -> address_typeof - edge address_typeof -> address_ref - edge address_ref -> @interface.lexical_scope - } - - ;; Modifiers are allowed in Solidity < 0.8.8 - if (version-matches "< 0.8.8") { - ;; Modifiers are available as a contract type members through a special '@modifier' guard - node modifier - node @interface.modifiers - attr (modifier) pop_symbol = "@modifier" - edge @interface.ns -> modifier - edge modifier -> @interface.modifiers - - ; We need "this" keyword is available in our lexical scope for modifier bodies to use - node this - attr (this) pop_symbol = "this" - edge @interface.lexical_scope -> this - edge this -> member - } -} - -@interface [InterfaceDefinition @specifier [InheritanceSpecifier]] { - let @specifier.heir = @interface - attr (@interface.def) parents = @specifier.parent_refs -} - -@interface [InterfaceDefinition [InterfaceMembers - [ContractMember @member ( - [EnumDefinition] - | [FunctionDefinition] - | [StructDefinition] - | [EventDefinition] - | [ErrorDefinition] - | [UserDefinedValueTypeDefinition] - )] -]] { - edge @member.lexical_scope -> @interface.lexical_scope - edge @interface.ns -> @member.def -} - -@interface [InterfaceDefinition [InterfaceMembers - [ContractMember @modifier [ModifierDefinition]] -]] { - ;; Modifiers are allowed in Solidity < 0.8.8 - if (version-matches "< 0.8.8") { - edge @modifier.lexical_scope -> @interface.lexical_scope - - ; Modifiers live in their own special scope - edge @interface.modifiers -> @modifier.def - - ;; This may prioritize this definition (when there are multiple options) - ;; according to the C3 linerisation ordering - attr (@modifier.def) parents = [@interface.def] - } -} - -;; Allow references (eg. variables of the interface type) to the interface to -;; access functions -@interface [InterfaceDefinition members: [InterfaceMembers - item: [ContractMember @function variant: [FunctionDefinition]] -]] { - edge @interface.members -> @function.def -} - -[InterfaceDefinition [InterfaceMembers [ContractMember @using [UsingDirective]]]] { - if (version-matches ">= 0.7.1") { - ; using directives are not allowed in interfaces after Solidity 0.7.1, but - ; the grammar allows them so we need to create an artificial node here to - ; connect to created edges from the instance nodes - let @using.lexical_scope = (node) - } -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Libraries -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@library [LibraryDefinition @name name: [Identifier]] { - node @library.lexical_scope - node @library.extensions - node @library.def - node @library.ns - node @library.modifiers - - attr (@library.def) node_definition = @name - attr (@library.def) definiens_node = @library - ; The .extensions node is where `using` directives will hook the definitions - attr (@library.def) extension_scope = @library.extensions - attr (@library.extensions) is_exported - - edge @library.lexical_scope -> @library.ns - - let @library.enclosing_def = @library.def - - node member - attr (member) pop_symbol = "." - edge @library.def -> member - edge member -> @library.ns - - ; Access to modifiers is guarded by a @modifier symbol - node modifier - attr (modifier) pop_symbol = "@modifier" - edge @library.ns -> modifier - edge modifier -> @library.modifiers - - ; Path to resolve the built-in type for type() expressions (same as contracts) - node type - attr (type) pop_symbol = "@type" - node type_library_type - attr (type_library_type) push_symbol = "%ContractTypeType" - edge @library.def -> type - edge type -> type_library_type - edge type_library_type -> @library.lexical_scope - - ; This is the connection point to resolve attached functions by `using for *` - node @library.star_extension - attr (@library.star_extension) push_symbol = "@*" - - if (version-matches "< 0.5.0") { - ; For Solidity < 0.5.0 there is a `this` in the lexical scope inside - ; libraries that acts as an `address` - node this - attr (this) pop_symbol = "this" - node address_typeof - attr (address_typeof) push_symbol = "@typeof" - node address_ref - attr (address_ref) push_symbol = "address" - - edge @library.lexical_scope -> this - edge this -> address_typeof - edge address_typeof -> address_ref - edge address_ref -> @library.lexical_scope - } -} - -@library [LibraryDefinition [LibraryMembers - [ContractMember @member ( - [EnumDefinition] - | [StructDefinition] - | [EventDefinition] - | [ErrorDefinition] - | [UserDefinedValueTypeDefinition] - )] -]] { - edge @member.lexical_scope -> @library.lexical_scope - edge @library.ns -> @member.def -} - -@library [LibraryDefinition [LibraryMembers - [ContractMember @member ( - [FunctionDefinition] - | [StateVariableDefinition [StateVariableAttributes [StateVariableAttribute [ConstantKeyword]]]] - )] -]] { - edge @member.lexical_scope -> @library.lexical_scope - edge @library.ns -> @member.def -} - -@library [LibraryDefinition [LibraryMembers - [ContractMember @modifier [ModifierDefinition]] -]] { - edge @library.modifiers -> @modifier.def - edge @modifier.lexical_scope -> @library.lexical_scope -} - -@library [LibraryDefinition [LibraryMembers - [ContractMember @using [UsingDirective]] -]] { - ; Expose the using directive from the extensions scope - edge @library.extensions -> @using.def -} - -@library [LibraryDefinition [LibraryMembers [ContractMember - [UsingDirective [UsingTarget [Asterisk]]] -]]] { - ; Connect the star extension node to the resolution extended scope if there is - ; a `using for *` directive in the library - edge @library.star_extension -> @library.lexical_scope -} - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Using directives -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@using [UsingDirective] { - ; This node acts as a definition in the sense that provides an entry point - ; that pops the target type and pushes the library/functions to attach to the - ; target type - node @using.def - - ; This internal node connects the definition side of the clause to the target - ; for resolution, and allows handling the multiple cases of `using` syntax - ; easily - node @using.clause -} - -@using [UsingDirective [UsingClause @id_path [IdentifierPath]]] { - ; resolve the library to be used in the directive - edge @id_path.push_end -> @using.lexical_scope - - ; because we're using the whole library, we don't need to "consume" the - ; attached function (as when using the deconstruction syntax), but we still - ; need to verify that we're only using this path when resolving a function - ; access to the target type, not the target type itself - node dot_guard_pop - attr (dot_guard_pop) pop_symbol = "." - node dot_guard_push - attr (dot_guard_push) push_symbol = "." - - edge @using.clause -> dot_guard_pop - edge dot_guard_pop -> dot_guard_push - edge dot_guard_push -> @id_path.push_begin -} - -@using [UsingDirective [UsingClause [UsingDeconstruction - [UsingDeconstructionSymbols [UsingDeconstructionSymbol - @id_path [IdentifierPath] - ]] -]]] { - ; resolve the function to be used in the directive - edge @id_path.push_end -> @using.lexical_scope - - node dot - attr (dot) pop_symbol = "." - node last_identifier - attr (last_identifier) pop_symbol = (source-text @id_path.rightmost_identifier) - - edge @using.clause -> dot - edge dot -> last_identifier - edge last_identifier -> @id_path.push_begin -} - -@using [UsingDirective [UsingTarget @type_name [TypeName]]] { - ; pop the type symbols to connect to the attached function (via @using.clause) - node typeof - attr (typeof) pop_symbol = "@typeof" - node cast - attr (cast) pop_symbol = "()" - - ; We connect to all_pop_begin to be able to resolve both qualified and - ; unqualified instances of the target type - edge @using.def -> @type_name.all_pop_begin - edge @type_name.pop_end -> typeof - edge typeof -> @using.clause - edge @type_name.pop_end -> cast - edge cast -> @using.clause - - ; resolve the target type of the directive on the lexical scope - edge @type_name.type_ref -> @using.lexical_scope -} - -[ContractMember @using [UsingDirective [UsingTarget [Asterisk]]]] { - ; using X for * is only allowed inside contracts - node star - attr (star) pop_symbol = "@*" - edge @using.def -> star - edge star -> @using.clause -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Type names -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; TypeName nodes should define these scoped variables: -;; -;; - @type_name.type_ref represents the node in the graph where we're ready to -;; resolve the type, and thus should generally be connected to a (lexical) -;; scope node (source node, outside edges connect *from* here). -;; -;; - @type_name.output represents the other end of the type and corresponds to a -;; state where the type has already been resolved so we can, for example -;; resolve its members (sink node, outside edges connect *to* here). -;; -;; - @type_name.pop_begin, @type_name.pop_end are used in a definition context, -;; ie. when we need to pop the type name symbol(s) from the symbol stack. -;; Additionally, @type_name.all_pop_begin links to each symbol in a typename -;; (ie. in an identifier path typename), which allows referring to a type both -;; qualified and unqualified. - -@type_name [TypeName @elementary [ElementaryType]] { - let @type_name.type_ref = @elementary.ref - let @type_name.output = @elementary.ref - let @type_name.pop_begin = @elementary.pop - let @type_name.pop_end = @elementary.pop - let @type_name.all_pop_begin = @elementary.pop -} - -@type_name [TypeName @id_path [IdentifierPath]] { - ;; For an identifier path used as a type, the left-most element is the one - ;; that connects to the parent lexical scope, because the name resolution - ;; starts at the left of the identifier. - let @type_name.type_ref = @id_path.push_end - - ;; Conversely, the complete type is found at the right-most name, and that's - ;; where users of this type should link to (eg. a variable declaration). - let @type_name.output = @id_path.push_begin - - let @type_name.pop_begin = @id_path.pop_begin - let @type_name.pop_end = @id_path.pop_end - let @type_name.all_pop_begin = @id_path.all_pop_begin -} - -@type_name [TypeName @type_variant ([ArrayTypeName] | [FunctionType])] { - let @type_name.type_ref = @type_variant.lexical_scope - let @type_name.output = @type_variant.output - let @type_name.pop_begin = @type_variant.pop_begin - let @type_name.pop_end = @type_variant.pop_end - let @type_name.all_pop_begin = @type_variant.pop_begin -} - -@type_name [TypeName @mapping [MappingType]] { - let @type_name.type_ref = @mapping.lexical_scope - let @type_name.output = @mapping.output - let @type_name.pop_begin = @mapping.pop_begin - let @type_name.pop_end = @mapping.pop_end - let @type_name.all_pop_begin = @mapping.pop_begin -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Elementary types -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@elementary [ElementaryType] { - node @elementary.ref - attr (@elementary.ref) type = "push_symbol" - attr (@elementary.ref) source_node = @elementary, symbol = @elementary.symbol - - node @elementary.pop - attr (@elementary.pop) pop_symbol = @elementary.symbol - - ; These variables are a bit redundant, but necessary to easily use elementary - ; types as mapping keys - let @elementary.pop_begin = @elementary.pop - let @elementary.pop_end = @elementary.pop - let @elementary.all_pop_begin = @elementary.pop - - let @elementary.push_begin = @elementary.ref - let @elementary.push_end = @elementary.ref -} - -@elementary [ElementaryType [AddressType . [AddressKeyword] .]] { - let @elementary.symbol = "address" -} - -@elementary [ElementaryType [AddressType . [AddressKeyword] [PayableKeyword] .]] { - let @elementary.symbol = "address payable" -} - -@elementary [ElementaryType [BoolKeyword]] { - let @elementary.symbol = "bool" -} - -@elementary [ElementaryType [ByteKeyword]] { - let @elementary.symbol = "byte" -} - -@elementary [ElementaryType @keyword [BytesKeyword]] { - let @elementary.symbol = (source-text @keyword) -} - -@elementary [ElementaryType [StringKeyword]] { - let @elementary.symbol = "string" -} - -@elementary [ElementaryType @keyword [IntKeyword]] { - let symbol = (source-text @keyword) - if (eq symbol "int") { - let @elementary.symbol = "int256" - } else { - let @elementary.symbol = symbol - } -} - -@elementary [ElementaryType @keyword [UintKeyword]] { - let symbol = (source-text @keyword) - if (eq symbol "uint") { - let @elementary.symbol = "uint256" - } else { - let @elementary.symbol = symbol - } -} - -@elementary [ElementaryType @keyword [FixedKeyword]] { - let symbol = (source-text @keyword) - if (eq symbol "fixed") { - let @elementary.symbol = "fixed128x18" - } else { - let @elementary.symbol = symbol - } -} - -@elementary [ElementaryType @keyword [UfixedKeyword]] { - let symbol = (source-text @keyword) - if (eq symbol "ufixed") { - let @elementary.symbol = "ufixed128x18" - } else { - let @elementary.symbol = symbol - } -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Mappings -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; When there's a parsing error (like a name in parameters for versions below 0.8.18) -;;; we still need the basics to get bindings working -@mapping [MappingType] { - node @mapping.lexical_scope - node @mapping.output - node @mapping.pop_begin - node @mapping.pop_end -} - -@mapping [MappingType - [MappingKey [MappingKeyType @key_type ([IdentifierPath] | [ElementaryType])]] - [MappingValue @value_type [TypeName]] -] { - ; Define the pushing path of the mapping type - ; ValueType <- top of the symbol stack - ; KeyType - ; %mapping <- bottom of the symbol stack - node mapping - attr (mapping) push_symbol = "%Mapping" - edge @mapping.output -> mapping - edge mapping -> @key_type.push_begin - edge @key_type.push_end -> @value_type.output - - ; Both key and value types need to be resolved - edge @value_type.type_ref -> @mapping.lexical_scope - edge @key_type.push_end -> @mapping.lexical_scope - - ; The mapping's type exposes the `[]` operator that returns the value type. - - node typeof_input - attr (typeof_input) pop_symbol = "@typeof" - edge @mapping.output -> typeof_input - - node typeof_output - attr (typeof_output) push_symbol = "@typeof" - edge typeof_output -> @value_type.output - - node index - attr (index) pop_symbol = "[]" - edge typeof_input -> index - edge index -> typeof_output - - ; Special case for mapping public state variables: they can be called - ; like a function with a key, and it's effectively the same as indexing it. - node getter_call - attr (getter_call) pop_symbol = "@as_getter" - edge typeof_input -> getter_call - edge getter_call -> typeof_output - ; We also want to recurse if the value type is a nested mapping/array - node nested_getter_call - attr (nested_getter_call) push_symbol = "@as_getter" - edge getter_call -> nested_getter_call - edge nested_getter_call -> typeof_output - - ; Now we define the "definition" route (aka. the pop route), to use in `using` directives only - ; This is the reverse of the pushing path above (to the `.output` node) - node pop_mapping - attr (pop_mapping) pop_symbol = "%Mapping" - - edge @mapping.pop_begin -> @value_type.pop_begin - edge @value_type.pop_end -> @key_type.pop_begin - edge @key_type.pop_end -> pop_mapping - edge pop_mapping -> @mapping.pop_end - - node @mapping.defs -} - -@mapping [MappingType - @key [MappingKey [MappingKeyType] @key_name [Identifier]] - [MappingValue] -] { - node @key.def - attr (@key.def) node_definition = @key_name - attr (@key.def) definiens_node = @key - edge @key.def -> @mapping.defs -} - -@mapping [MappingType - [MappingKey] - @value [MappingValue [TypeName] @value_name [Identifier]] -] { - node @value.def - attr (@value.def) node_definition = @value_name - attr (@value.def) definiens_node = @value - edge @value.def -> @mapping.defs -} - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Arrays types -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@array [ArrayTypeName] { - node @array.lexical_scope - node @array.output -} - -@array [ArrayTypeName [TypeName] index: [Expression]] { - let @array.type_symbol = "%FixedArray" -} - -@array [ArrayTypeName [OpenBracket] . [CloseBracket]] { - let @array.type_symbol = "%Array" -} - -@array [ArrayTypeName @type_name [TypeName]] { - ; Define the pushing path of the array type - ; ValueType <- top of the symbol stack - ; @typeof - ; %array / %arrayFixed <- bottom of the symbol stack - node array - attr (array) push_symbol = @array.type_symbol - node item_typeof - attr (item_typeof) push_symbol = "@typeof" - edge @array.output -> array - edge array -> item_typeof - edge item_typeof -> @type_name.output - - ; Resolve the value type itself - edge @type_name.type_ref -> @array.lexical_scope - ; And also the "type erased" array type so we can resolve built-in members - edge array -> @array.lexical_scope - - ; Define the path to resolve index access (aka the `[]` operator) - - node typeof_input - attr (typeof_input) pop_symbol = "@typeof" - edge @array.output -> typeof_input - - node typeof_output - attr (typeof_output) push_symbol = "@typeof" - edge typeof_output -> @type_name.output - - node index - attr (index) pop_symbol = "[]" - edge typeof_input -> index - edge index -> typeof_output - - ; Special case for public state variables of type array: they can be called - ; like a function with an index, and it's effectively the same as indexing the - ; array. - node getter_call - attr (getter_call) pop_symbol = "@as_getter" - edge typeof_input -> getter_call - edge getter_call -> typeof_output - ; We also want to recurse if the value type is a nested mapping/array - node nested_getter_call - attr (nested_getter_call) push_symbol = "@as_getter" - edge getter_call -> nested_getter_call - edge nested_getter_call -> typeof_output - - ; Define the special `.push()` built-in that returns the element type (for Solidity >= 0.6.0) - if (version-matches ">= 0.6.0") { - node built_in_member - attr (built_in_member) pop_symbol = "." - node push_built_in - attr (push_built_in) pop_symbol = "push" - node built_in_call - attr (built_in_call) pop_symbol = "()" - - edge typeof_input -> built_in_member - edge built_in_member -> push_built_in - edge push_built_in -> built_in_call - edge built_in_call -> typeof_output - } - - ; Now we define the "definition" route (aka. the pop route), to use in `using` directives only - ; This is essentially the reverse of the second path above - node pop_item_typeof - attr (pop_item_typeof) pop_symbol = "@typeof" - node pop_array - attr (pop_array) pop_symbol = @array.type_symbol - - let @array.pop_begin = @type_name.pop_begin - edge @type_name.pop_end -> pop_item_typeof - edge pop_item_typeof -> pop_array - let @array.pop_end = pop_array -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Function types -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@ftype [FunctionType] { - node @ftype.lexical_scope - node @ftype.output -} - -@ftype [FunctionType @attrs [FunctionTypeAttributes]] { - ; Compute the built-in type of the function - ; %functionExternal provides access to .selector and .address - var type_symbol = "%Function" - scan (source-text @attrs) { - "external" { - set type_symbol = "%ExternalFunction" - } - } - - ; This path pushes the function type to the symbol stack - ; Currently, these don't add parameter and return types to distinguish between different function types - ; https://github.com/NomicFoundation/slang/issues/1250 - node function_type - attr (function_type) push_symbol = type_symbol - - edge @ftype.output -> function_type - edge function_type -> @ftype.lexical_scope - - ; the pop path for the using directive - node pop_function_type - attr (pop_function_type) pop_symbol = type_symbol - - let @ftype.pop_begin = pop_function_type - let @ftype.pop_end = pop_function_type -} - -@ftype [FunctionType @params [ParametersDeclaration]] { - edge @params.lexical_scope -> @ftype.lexical_scope -} - -@ftype [FunctionType [ReturnsDeclaration @return_params [ParametersDeclaration]]] { - edge @return_params.lexical_scope -> @ftype.lexical_scope -} - -@ftype [FunctionType [ReturnsDeclaration - [ParametersDeclaration [Parameters . @param [Parameter] .]] -]] { - ; Variables of a function type type can be "called" and resolve to the type of - ; the return parameter. This is only valid if the function returns a single - ; value. - node typeof - attr (typeof) pop_symbol = "@typeof" - - node call - attr (call) pop_symbol = "()" - - edge @ftype.output -> typeof - edge typeof -> call - edge call -> @param.typeof -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Identifier Paths (aka. references to custom types) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; The identifier path builds two graph paths: -;; -;; - From right to left, pushing the identifiers and acting as a "reference". -;; This path begins at @id_path.push_begin and ends at @id_path.push_end. -;; -;; - From left to right, popping the identifiers (used as a definition sink in -;; using directives). This path begins at @id_path.pop_begin and ends at -;; @id_path.pop_end. -;; -;; NOTE: most of the time, and unless this identifier path is the target of a -;; using directive this second path will not be used and will form a -;; disconnected graph component. We currently have no way of determining when -;; this path is necessary, so we always construct it. -;; -;; Additionally the IdentifierPath defines another scoped variable -;; @id_path.rightmost_identifier which corresponds to the identifier in the last -;; position in the path, from left to right. This is used in the using directive -;; rules to be able to pop the name of the attached function. -;; -;; They also provide a `.push_ns` node which points to the right-most 'period' -;; separating the rightmost identifier from the "namespace" qualifying part of -;; the path. This allows creating additional referencing paths with guards right -;; before the unqualified identifier (eg. for modifiers). - -@id_path [IdentifierPath] { - ; This node connects to all parts of the path, for popping. This allows to - ; connect at any point of the path. Useful for `using` directives when the - ; target type is fully qualified but we want to resolve for the unqualified - ; name. - node @id_path.all_pop_begin - node @id_path.push_end -} - -@id_path [IdentifierPath @name [Identifier]] { - node @name.ref - attr (@name.ref) node_reference = @name - attr (@name.ref) parents = [@id_path.enclosing_def] - - node @name.pop - attr (@name.pop) pop_symbol = (source-text @name) - - edge @id_path.all_pop_begin -> @name.pop -} - -; Single identifier paths -@id_path [IdentifierPath . @name [Identifier] .] { - let @id_path.rightmost_identifier = @name - - let @id_path.push_begin = @name.ref - let @id_path.push_ns = (node) - edge @name.ref -> @id_path.push_ns - edge @id_path.push_ns -> @id_path.push_end - - let @id_path.pop_begin = @name.pop - let @id_path.pop_end = @name.pop -} - -; Multiple identifier paths -@id_path [IdentifierPath [_] . @name [Identifier] .] { - let @id_path.rightmost_identifier = @name - - let @id_path.push_begin = @name.ref - let @id_path.pop_end = @name.pop -} - -[IdentifierPath @left_name [Identifier] . @period [Period] . @right_name [Identifier]] { - node @period.ref_member - attr (@period.ref_member) push_symbol = "." - - edge @right_name.ref -> @period.ref_member - edge @period.ref_member -> @left_name.ref - - node @period.pop_member - attr (@period.pop_member) pop_symbol = "." - - edge @left_name.pop -> @period.pop_member - edge @period.pop_member -> @right_name.pop -} - -@id_path [IdentifierPath @last_period [Period] . [Identifier] . ] { - let @id_path.push_ns = @last_period.ref_member -} - -@id_path [IdentifierPath . @name [Identifier] . [_]] { - edge @name.ref -> @id_path.push_end - let @id_path.pop_begin = @name.pop -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Function, parameter declarations and modifiers -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@param [Parameter @type_name [TypeName]] { - node @param.lexical_scope - node @param.def - - edge @type_name.type_ref -> @param.lexical_scope - - node @param.typeof - attr (@param.typeof) push_symbol = "@typeof" - edge @param.typeof -> @type_name.output -} - -@param [Parameter @name [Identifier]] { - attr (@param.def) node_definition = @name - attr (@param.def) definiens_node = @param - - edge @param.def -> @param.typeof -} - -@params [ParametersDeclaration] { - node @params.lexical_scope - node @params.defs - - ;; This scope can be used to resolve named argument calls - node @params.names - attr (@params.names) pop_symbol = "@param_names" - edge @params.names -> @params.defs -} - -@params [ParametersDeclaration [Parameters @param item: [Parameter]]] { - edge @param.lexical_scope -> @params.lexical_scope - edge @params.defs -> @param.def -} - -@function [FunctionDefinition @attrs [FunctionAttributes]] { - var type_symbol = "%Function" - scan (source-text @attrs) { - "\\b(public|external)\\b" { - set type_symbol = "%ExternalFunction" - } - } - - node @function.lexical_scope - node @function.def - - ; this path from the function definition to the scope allows attaching - ; functions to this function's type - node typeof - attr (typeof) push_symbol = "@typeof" - node type_function - attr (type_function) push_symbol = type_symbol - edge @function.def -> typeof - edge typeof -> type_function - edge type_function -> @function.lexical_scope - - ;; This scope provides access to local variables from Yul assembly blocks - node @function.yul_locals - attr (@function.yul_locals) pop_symbol = "@yul_locals" - edge @function.lexical_scope -> @function.yul_locals -} - -@function [FunctionDefinition name: [FunctionName @name [Identifier]]] { - attr (@function.def) node_definition = @name - attr (@function.def) definiens_node = @function -} - -@function [FunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @function.lexical_scope - - ;; Input parameters are available in the function scope - edge @function.lexical_scope -> @params.defs - ;; ... and shadow other declarations - attr (@function.lexical_scope -> @params.defs) precedence = 1 - - ;; Function parameters should also be able inside Yul assembly blocks via a guarded path - edge @function.yul_locals -> @params.defs - - ;; Connect to parameters for named argument resolution - edge @function.def -> @params.names -} - -@function [FunctionDefinition returns: [ReturnsDeclaration - @return_params [ParametersDeclaration] -]] { - edge @return_params.lexical_scope -> @function.lexical_scope - - ;; Return parameters are available in the function scope - edge @function.lexical_scope -> @return_params.defs - ;; ... and shadow other declarations - attr (@function.lexical_scope -> @return_params.defs) precedence = 1 - - ;; Function return parameters should also be able inside Yul assembly blocks via a guarded path - edge @function.yul_locals -> @return_params.defs -} - -;; Only functions that return a single value have an actual return type -;; since tuples are not actual types in Solidity -@function [FunctionDefinition returns: [ReturnsDeclaration - [ParametersDeclaration [Parameters . @param [Parameter] .]] -]] { - node call - attr (call) pop_symbol = "()" - - edge @function.def -> call - edge call -> @param.typeof -} - -;; Connect the function body's block lexical scope to the function -@function [FunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @function.lexical_scope -} - -@function [FunctionDefinition [FunctionAttributes item: [FunctionAttribute - @modifier [ModifierInvocation] -]]] { - edge @modifier.lexical_scope -> @function.lexical_scope -} - -@modifier [ModifierInvocation @name [IdentifierPath]] { - node @modifier.lexical_scope - - node guard_pop - attr (guard_pop) pop_symbol = (source-text @name.rightmost_identifier) - node guard_push - attr (guard_push) push_symbol = (source-text @name.rightmost_identifier) - node modifier - attr (modifier) push_symbol = "@modifier" - - edge @name.push_begin -> guard_pop - edge guard_pop -> guard_push - edge guard_push -> modifier - edge modifier -> @name.push_ns - edge @name.push_end -> @modifier.lexical_scope - - ; This allows resolving @name in the more general scope in constructors (since - ; calling a parent constructor is parsed as a modifier invocation) - let @modifier.identifier = @name.push_end -} - -@modifier [ModifierInvocation @args [ArgumentsDeclaration]] { - edge @args.lexical_scope -> @modifier.lexical_scope -} - -;;; Unnamed functions (deprecated) -@unnamed_function [UnnamedFunctionDefinition] { - node @unnamed_function.lexical_scope -} - -@unnamed_function [UnnamedFunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @unnamed_function.lexical_scope - - edge @unnamed_function.lexical_scope -> @params.defs - attr (@unnamed_function.lexical_scope -> @params.defs) precedence = 1 -} - -@unnamed_function [UnnamedFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @unnamed_function.lexical_scope -} - -@unnamed_function [UnnamedFunctionDefinition - [UnnamedFunctionAttributes [UnnamedFunctionAttribute @modifier [ModifierInvocation]]] -] { - edge @modifier.lexical_scope -> @unnamed_function.lexical_scope -} - - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Constructors -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@constructor [ConstructorDefinition] { - node @constructor.lexical_scope - node @constructor.def - - ;; This scope provides access to local variables from Yul assembly blocks - node @constructor.yul_locals - attr (@constructor.yul_locals) pop_symbol = "@yul_locals" - edge @constructor.lexical_scope -> @constructor.yul_locals -} - -@constructor [ConstructorDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @constructor.lexical_scope - - ;; Input parameters are available in the constructor scope - edge @constructor.lexical_scope -> @params.defs - ;; ... and shadow other declarations - attr (@constructor.lexical_scope -> @params.defs) precedence = 1 - - ;; Connect to parameters for named argument resolution - edge @constructor.def -> @params.names - - ;; Parameters should be accessible to assembly blocks - edge @constructor.yul_locals -> @params.defs -} - -;; Connect the constructor body's block lexical scope to the constructor -@constructor [ConstructorDefinition @block [Block]] { - edge @block.lexical_scope -> @constructor.lexical_scope -} - -@constructor [ConstructorDefinition [ConstructorAttributes item: [ConstructorAttribute - @modifier [ModifierInvocation] -]]] { - edge @modifier.lexical_scope -> @constructor.lexical_scope - edge @modifier.identifier -> @constructor.lexical_scope -} - -@contract [ContractDefinition [ContractMembers [ContractMember - @constructor [ConstructorDefinition] -]]] { - ;; This link allows calling a constructor with the named parameters syntax - edge @contract.def -> @constructor.def -} - -;; Solidity < 0.5.0 constructors -;; They were declared as functions of the contract's name - -@contract [ContractDefinition - @contract_name [Identifier] - [ContractMembers [ContractMember [FunctionDefinition - [FunctionName @function_name [Identifier]] - @params [ParametersDeclaration] - ]]] -] { - if (version-matches "< 0.5.0") { - if (eq (source-text @contract_name) (source-text @function_name)) { - ; Connect to parameters for named argument resolution - edge @contract.def -> @params.names - } - } -} - -[ContractDefinition - @contract_name [Identifier] - [ContractMembers [ContractMember @function [FunctionDefinition - [FunctionName @function_name [Identifier]] - [FunctionAttributes [FunctionAttribute @modifier [ModifierInvocation]]] - ]]] -] { - if (version-matches "< 0.5.0") { - if (eq (source-text @contract_name) (source-text @function_name)) { - ; Parent constructor calls are parsed as modifier invocations - edge @modifier.identifier -> @function.lexical_scope - } - } -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Fallback and receive functions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@fallback [FallbackFunctionDefinition] { - node @fallback.lexical_scope - - ;; This scope provides access to local variables from Yul assembly blocks - node @fallback.yul_locals - attr (@fallback.yul_locals) pop_symbol = "@yul_locals" - edge @fallback.lexical_scope -> @fallback.yul_locals -} - -@fallback [FallbackFunctionDefinition @params parameters: [ParametersDeclaration]] { - edge @params.lexical_scope -> @fallback.lexical_scope - - ;; Input parameters are available in the fallback function scope - edge @fallback.lexical_scope -> @params.defs - edge @fallback.yul_locals -> @params.defs - attr (@fallback.lexical_scope -> @params.defs) precedence = 1 -} - -@fallback [FallbackFunctionDefinition returns: [ReturnsDeclaration - @return_params [ParametersDeclaration] -]] { - edge @return_params.lexical_scope -> @fallback.lexical_scope - - ;; Return parameters are available in the fallback function scope - edge @fallback.lexical_scope -> @return_params.defs - edge @fallback.yul_locals -> @return_params.defs - attr (@fallback.lexical_scope -> @return_params.defs) precedence = 1 -} - -@fallback [FallbackFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @fallback.lexical_scope -} - -@fallback [FallbackFunctionDefinition [FallbackFunctionAttributes - item: [FallbackFunctionAttribute @modifier [ModifierInvocation]] -]] { - edge @modifier.lexical_scope -> @fallback.lexical_scope -} - -@receive [ReceiveFunctionDefinition] { - node @receive.lexical_scope -} - -@receive [ReceiveFunctionDefinition [FunctionBody @block [Block]]] { - edge @block.lexical_scope -> @receive.lexical_scope -} - -@receive [ReceiveFunctionDefinition [ReceiveFunctionAttributes - item: [ReceiveFunctionAttribute @modifier [ModifierInvocation]] -]] { - edge @modifier.lexical_scope -> @receive.lexical_scope -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Function modifiers -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@modifier [ModifierDefinition] { - node @modifier.def - node @modifier.lexical_scope - - ;; This scope provides access to local variables from Yul assembly blocks - node @modifier.yul_locals - attr (@modifier.yul_locals) pop_symbol = "@yul_locals" - edge @modifier.lexical_scope -> @modifier.yul_locals -} - -@modifier [ModifierDefinition - @name name: [Identifier] -] { - attr (@modifier.def) node_definition = @name - attr (@modifier.def) definiens_node = @modifier -} - -@modifier [ModifierDefinition - body: [FunctionBody @body [Block]] -] { - edge @body.lexical_scope -> @modifier.lexical_scope - - ;; Special case: bind the place holder statement `_` to the built-in - ;; `%_`. This only happens in the body of a modifier. - node placeholder_pop - attr (placeholder_pop) pop_symbol = "_" - node placeholder_ref - attr (placeholder_ref) push_symbol = "%_" - - edge @body.lexical_scope -> placeholder_pop - edge placeholder_pop -> placeholder_ref - edge placeholder_ref -> @modifier.lexical_scope - ; Ensure we only bind to the built-in - attr (@body.lexical_scope -> placeholder_pop) precedence = 1 -} - -@modifier [ModifierDefinition @params [ParametersDeclaration]] { - edge @params.lexical_scope -> @modifier.lexical_scope - - ;; Input parameters are available in the modifier scope - edge @modifier.lexical_scope -> @params.defs - attr (@modifier.lexical_scope -> @params.defs) precedence = 1 - - ;; Input parameters are also available to assembly blocks - edge @modifier.yul_locals -> @params.defs -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Blocks and generic statements -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@block [Block] { - node @block.lexical_scope - node @block.defs -} - -;; The first statement in a block -@block [Block [Statements . @stmt [Statement]]] { - if (version-matches ">= 0.5.0") { - edge @stmt.lexical_scope -> @block.lexical_scope - } -} - -@block [Block [Statements @stmt [Statement]]] { - ;; Hoist statement definitions for Solidity < 0.5.0 - if (version-matches "< 0.5.0") { - ;; definitions are carried over to the block - edge @block.defs -> @stmt.defs - - ;; resolution happens in the context of the block - edge @stmt.lexical_scope -> @block.lexical_scope - - ;; and the statement definitions are available block's scope - edge @block.lexical_scope -> @stmt.defs - ;; ... shadowing declarations in enclosing scopes - attr (@block.lexical_scope -> @stmt.defs) precedence = 1 - - ;; Block definitions are also available to Yul assembly blocks - node yul_locals - attr (yul_locals) pop_symbol = "@yul_locals" - edge @block.lexical_scope -> yul_locals - edge yul_locals -> @stmt.defs - } -} - -;; Two consecutive statements -[Statements @left_stmt [Statement] . @right_stmt [Statement]] { - if (version-matches ">= 0.5.0") { - edge @right_stmt.lexical_scope -> @left_stmt.lexical_scope - } -} - -@stmt [Statement] { - node @stmt.lexical_scope - node @stmt.defs - - if (version-matches ">= 0.5.0") { - ;; For Solidity >= 0.5.0, definitions are immediately available in the - ;; statement scope. For < 0.5.0 this is also true, but resolved through the - ;; enclosing block's lexical scope. - edge @stmt.lexical_scope -> @stmt.defs - ;; Statement definitions shadow other declarations in its scope - attr (@stmt.lexical_scope -> @stmt.defs) precedence = 1 - } -} - -;; Statements of type block -@stmt [Statement @block variant: [Block]] { - edge @block.lexical_scope -> @stmt.lexical_scope - - ;; Hoist block definitions (< 0.5.0) - if (version-matches "< 0.5.0") { - edge @stmt.defs -> @block.defs - } -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Expressions & declaration statements -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; In general for statements the structure is [Statement [StmtVariant]] and we -;; will define the scoped nodes .lexical_scope and (possibly) .defs in the -;; Statement CST node, skipping scoped nodes in the variant of the statement. -;; -;; For expression statements, variable and tuple declarations we define them -;; separately from the enclosing statement to be able to use them in `for` -;; initialization and condition clauses directly. Also, because we intend to -;; reuse them, all of them must have both a .lexical_scope and .defs scoped -;; nodes (even though .defs doesn't make sense for ExpressionStatement) - -@stmt [Statement @expr_stmt [ExpressionStatement]] { - edge @expr_stmt.lexical_scope -> @stmt.lexical_scope -} - -@expr_stmt [ExpressionStatement] { - node @expr_stmt.lexical_scope -} - - -;;; Variable declaration statements - -@stmt [Statement @var_decl [VariableDeclarationStatement]] { - edge @var_decl.lexical_scope -> @stmt.lexical_scope - edge @stmt.defs -> @var_decl.def - - ;; Make the variable available to Yul assembly blocks through a guarded path - node yul_locals - attr (yul_locals) pop_symbol = "@yul_locals" - edge @stmt.lexical_scope -> yul_locals - edge yul_locals -> @var_decl.def -} - -@var_decl [VariableDeclarationStatement] { - node @var_decl.lexical_scope - node @var_decl.def -} - -@var_decl [VariableDeclarationStatement - [VariableDeclarationType @var_type [TypeName]] - @name name: [Identifier] -] { - attr (@var_decl.def) node_definition = @name - attr (@var_decl.def) definiens_node = @var_decl - - edge @var_type.type_ref -> @var_decl.lexical_scope - - node typeof - attr (typeof) push_symbol = "@typeof" - - edge @var_decl.def -> typeof - edge typeof -> @var_type.output -} - -@var_decl [VariableDeclarationStatement - [VariableDeclarationType [VarKeyword]] - @name name: [Identifier] -] { - attr (@var_decl.def) node_definition = @name - attr (@var_decl.def) definiens_node = @var_decl -} - -@var_decl [VariableDeclarationStatement - [VariableDeclarationType [VarKeyword]] - [VariableDeclarationValue @value [Expression]] -] { - edge @var_decl.def -> @value.output -} - - - -;;; Tuple deconstruction statements - -@stmt [Statement @tuple_decon [TupleDeconstructionStatement]] { - edge @tuple_decon.lexical_scope -> @stmt.lexical_scope - edge @stmt.defs -> @tuple_decon.defs - - ;; Make any variables available to Yul assembly blocks through a guarded path - node yul_locals - attr (yul_locals) pop_symbol = "@yul_locals" - edge @stmt.lexical_scope -> yul_locals - edge yul_locals -> @tuple_decon.defs -} - -@tuple_decon [TupleDeconstructionStatement] { - node @tuple_decon.lexical_scope - node @tuple_decon.defs -} - -;; Tuple deconstruction statements with no types and a `var` keyword are -;; declarations (only valid in Solidity < 0.5.0) -@tuple_decon [TupleDeconstructionStatement [VarKeyword] [TupleDeconstructionElements - [TupleDeconstructionElement - [TupleMember @tuple_member variant: [UntypedTupleMember - @name name: [Identifier]] - ] - ] -]] { - node def - attr (def) node_definition = @name - attr (def) definiens_node = @tuple_member - - edge @tuple_decon.defs -> def -} - -;; Tuple deconstruction statements with no types and no `var` keyword are assignments -@tuple_decon [TupleDeconstructionStatement . [OpenParen] [TupleDeconstructionElements - [TupleDeconstructionElement - [TupleMember variant: [UntypedTupleMember - @name name: [Identifier]] - ] - ] -]] { - node ref - attr (ref) node_reference = @name - - edge ref -> @tuple_decon.lexical_scope -} - -@tuple_decon [TupleDeconstructionStatement [TupleDeconstructionElements - [TupleDeconstructionElement - [TupleMember @tuple_member variant: [TypedTupleMember - @member_type type_name: [TypeName] - @name name: [Identifier]] - ] - ] -]] { - node def - attr (def) node_definition = @name - attr (def) definiens_node = @tuple_member - - edge @tuple_decon.defs -> def - edge @member_type.type_ref -> @tuple_decon.lexical_scope - - node typeof - attr (typeof) push_symbol = "@typeof" - - edge def -> typeof - edge typeof -> @member_type.output -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Control statements -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; If conditionals - -@stmt [Statement [IfStatement @body body: [Statement]]] { - edge @body.lexical_scope -> @stmt.lexical_scope - if (version-matches "< 0.5.0") { - edge @stmt.defs -> @body.defs - } -} - -@stmt [Statement [IfStatement else_branch: [ElseBranch @else_body body: [Statement]]]] { - edge @else_body.lexical_scope -> @stmt.lexical_scope - if (version-matches "< 0.5.0") { - edge @stmt.defs -> @else_body.defs - } -} - -;; For loops - -@stmt [Statement [ForStatement - initialization: [ForStatementInitialization @init_stmt [ExpressionStatement]] -]] { - edge @init_stmt.lexical_scope -> @stmt.lexical_scope -} - -@stmt [Statement [ForStatement - initialization: [ForStatementInitialization @init_stmt [VariableDeclarationStatement]] -]] { - edge @init_stmt.lexical_scope -> @stmt.lexical_scope - edge @stmt.init_defs -> @init_stmt.def -} - -@stmt [Statement [ForStatement - initialization: [ForStatementInitialization @init_stmt [TupleDeconstructionStatement]] -]] { - edge @init_stmt.lexical_scope -> @stmt.lexical_scope - edge @stmt.init_defs -> @init_stmt.defs -} - -@stmt [Statement [ForStatement - condition: [ForStatementCondition @cond_stmt [ExpressionStatement]] -]] { - edge @cond_stmt.lexical_scope -> @stmt.lexical_scope - edge @cond_stmt.lexical_scope -> @stmt.init_defs -} - -@stmt [Statement [ForStatement @iter_expr iterator: [Expression]]] { - ; for the iterator expression we need an independent scope node that can - ; connect to both the for-statement *and* the definitions in the init - ; expression - node @iter_expr.lexical_scope - edge @iter_expr.lexical_scope -> @stmt.lexical_scope - edge @iter_expr.lexical_scope -> @stmt.init_defs -} - -@stmt [Statement [ForStatement @body body: [Statement]]] { - node @stmt.init_defs - - edge @body.lexical_scope -> @stmt.lexical_scope - edge @body.lexical_scope -> @stmt.init_defs - if (version-matches "< 0.5.0") { - edge @stmt.defs -> @body.defs - edge @stmt.defs -> @stmt.init_defs - } - - ;; For initialization defs are also available to Yul assembly blocks - node yul_locals - attr (yul_locals) pop_symbol = "@yul_locals" - edge @body.lexical_scope -> yul_locals - edge yul_locals -> @stmt.init_defs -} - -;; While loops - -@stmt [Statement [WhileStatement @body body: [Statement]]] { - edge @body.lexical_scope -> @stmt.lexical_scope - if (version-matches "< 0.5.0") { - edge @stmt.defs -> @body.defs - } -} - -;; Do-while loops - -@stmt [Statement [DoWhileStatement @body body: [Statement]]] { - edge @body.lexical_scope -> @stmt.lexical_scope - if (version-matches "< 0.5.0") { - edge @stmt.defs -> @body.defs - } -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Error handling -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; Try-catch statements - -@stmt [Statement [TryStatement @body body: [Block]]] { - edge @body.lexical_scope -> @stmt.lexical_scope -} - -@stmt [Statement [TryStatement - [ReturnsDeclaration @return_params [ParametersDeclaration]] - @body body: [Block] -]] { - edge @return_params.lexical_scope -> @stmt.lexical_scope - edge @body.lexical_scope -> @return_params.defs - ;; Similar to functions, return params shadow other declarations - attr (@body.lexical_scope -> @return_params.defs) precedence = 1 - - ;; Return params are also available to Yul assembly blocks - node yul_locals - attr (yul_locals) pop_symbol = "@yul_locals" - edge @body.lexical_scope -> yul_locals - edge yul_locals -> @return_params.defs -} - -@stmt [Statement [TryStatement [CatchClauses [CatchClause - @body body: [Block] -]]]] { - edge @body.lexical_scope -> @stmt.lexical_scope -} - -@stmt [Statement [TryStatement [CatchClauses [CatchClause - [CatchClauseError @catch_params parameters: [ParametersDeclaration]] - @body body: [Block] -]]]] { - edge @catch_params.lexical_scope -> @stmt.lexical_scope - edge @body.lexical_scope -> @catch_params.defs - ;; Similar to functions, catch params shadow other declarations - attr (@body.lexical_scope -> @catch_params.defs) precedence = 1 - - ;; Catch params are also available to Yul assembly blocks - node yul_locals - attr (yul_locals) pop_symbol = "@yul_locals" - edge @body.lexical_scope -> yul_locals - edge yul_locals -> @catch_params.defs -} - -@stmt [Statement [TryStatement [CatchClauses [CatchClause - [CatchClauseError @name [Identifier]] -]]]] { - node ref - attr (ref) node_reference = @name - - edge ref -> @stmt.lexical_scope -} - - -;;; Revert statements - -@stmt [Statement [RevertStatement @error_ident [IdentifierPath]]] { - edge @error_ident.push_end -> @stmt.lexical_scope -} - -@stmt [Statement [RevertStatement @args [ArgumentsDeclaration]]] { - edge @args.lexical_scope -> @stmt.lexical_scope -} - -[Statement [RevertStatement - @error_ident [IdentifierPath] - @args [ArgumentsDeclaration] -]] { - edge @args.refs -> @error_ident.push_begin -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Other statements -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; Emit -@stmt [Statement [EmitStatement - @event_ident [IdentifierPath] - @args [ArgumentsDeclaration] -]] { - edge @event_ident.push_end -> @stmt.lexical_scope - edge @args.lexical_scope -> @stmt.lexical_scope - edge @args.refs -> @event_ident.push_begin -} - -;;; Unchecked -@stmt [Statement [UncheckedBlock @block block: [Block]]] { - edge @block.lexical_scope -> @stmt.lexical_scope -} - -;;; Assembly -@stmt [Statement [AssemblyStatement @body body: [YulBlock]]] { - ;; This provides a path to constants and Yul built-ins. - ;; __SLANG_SOLIDITY_YUL_BUILT_INS_GUARD__ keep in sync with built-ins code generation - node yul - attr (yul) push_symbol = "@yul" - edge @body.lexical_scope -> yul - edge yul -> @stmt.lexical_scope - - ;; Both should also be available inside Yul functions (which cannot see - ;; anything else from outer scopes) - node in_yul_function_pop - attr (in_yul_function_pop) pop_symbol = "@in_yul_function" - edge @body.lexical_scope -> in_yul_function_pop - edge in_yul_function_pop -> yul - - ;; Also provide access to locals and state variables from Solidity - node yul_locals - attr (yul_locals) push_symbol = "@yul_locals" - edge @body.lexical_scope -> yul_locals - edge yul_locals -> @stmt.lexical_scope -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; State Variables -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@state_var [StateVariableDefinition] { - node @state_var.lexical_scope - node @state_var.def - node @state_var.typeof -} - -@state_var [StateVariableDefinition - @type_name type_name: [TypeName] - @name name: [Identifier] -] { - attr (@state_var.def) node_definition = @name - attr (@state_var.def) definiens_node = @state_var - - edge @type_name.type_ref -> @state_var.lexical_scope - - attr (@state_var.typeof) push_symbol = "@typeof" - - edge @state_var.def -> @state_var.typeof - edge @state_var.typeof -> @type_name.output -} - -@state_var [StateVariableDefinition - [StateVariableAttributes [StateVariableAttribute [PublicKeyword]]] -] { - ; Public state variables are used as functions when invoked from an external contract - node call - attr (call) pop_symbol = "()" - - ; In the general case using the getter can bind to the state variable's type - edge @state_var.def -> call - edge call -> @state_var.typeof - - ; When used as a member of the container contract, the variable symbol refers - ; to an external function, and as such, we can obtain its address and - ; selector. Caveat: this will also bind in the case where the var is _not_ being - ; used as a function, but that's not valid Solidity. - node external_function - attr (external_function) push_symbol = "%ExternalFunction" - edge @state_var.typeof -> external_function - edge external_function -> @state_var.lexical_scope - - ; Some complex types generate special getters (ie. arrays and mappings index - ; their contents, structs flatten most of their fields and return a tuple) - node getter - attr (getter) push_symbol = "@as_getter" - edge call -> getter - edge getter -> @state_var.typeof -} - -@state_var [StateVariableDefinition - [StateVariableDefinitionValue @value [Expression]] -] { - let @value.lexical_scope = @state_var.lexical_scope -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Enum definitions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@enum [EnumDefinition @name name: [Identifier]] { - node @enum.lexical_scope - node @enum.def - node @enum.members - - attr (@enum.def) node_definition = @name - attr (@enum.def) definiens_node = @enum - - node member - attr (member) pop_symbol = "." - - edge @enum.def -> member - edge member -> @enum.members - - ; value_ref_typeof allows for an enum value to access the - ; functions associated to this type - node @enum.value_ref_typeof - attr (@enum.value_ref_typeof) push_symbol = "@typeof" - node value_ref_type - attr (value_ref_type) push_symbol = (source-text @name) - edge @enum.value_ref_typeof -> value_ref_type - edge value_ref_type -> @enum.lexical_scope - - ; Path to link min and max to the enum's scope (#1158) - ; It resolves paths of the form `type().min.` - if (version-matches ">= 0.8.8") { - node type - attr (type) pop_symbol = "@type" - node typeof - attr (typeof) pop_symbol = "@typeof" - - node built_in_member - attr (built_in_member) pop_symbol = "." - node min_built_in - attr (min_built_in) pop_symbol = "min" - node max_built_in - attr (max_built_in) pop_symbol = "max" - - edge @enum.def -> type - edge type -> typeof - edge typeof -> built_in_member - edge built_in_member -> min_built_in - edge built_in_member -> max_built_in - edge min_built_in -> @enum.value_ref_typeof - edge max_built_in -> @enum.value_ref_typeof - - ; Path to resolve the built-in members min and max - node type_enum_type - attr (type_enum_type) push_symbol = "%IntTypeType" - edge type -> type_enum_type - edge type_enum_type -> @enum.lexical_scope - } -} - -@enum [EnumDefinition - members: [EnumMembers @item [Identifier]] -] { - node def - attr (def) node_definition = @item - attr (def) definiens_node = @item - - edge def -> @enum.value_ref_typeof - edge @enum.members -> def -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Structure definitions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@struct [StructDefinition @name name: [Identifier]] { - node @struct.lexical_scope - node @struct.def - node @struct.members - - attr (@struct.def) node_definition = @name - attr (@struct.def) definiens_node = @struct - - ; Now connect normally to the struct members - node @struct.typeof - attr (@struct.typeof) pop_symbol = "@typeof" - node member - attr (member) pop_symbol = "." - edge @struct.def -> @struct.typeof - edge @struct.typeof -> member - edge member -> @struct.members - - ; Bind member names when using construction with named arguments - node param_names - attr (param_names) pop_symbol = "@param_names" - edge @struct.def -> param_names - edge param_names -> @struct.members - - ; Used as a function call (ie. casting), should bind to itself - node call - attr (call) pop_symbol = "()" - edge @struct.def -> call - edge call -> member -} - -@member [StructMember] { - node @member.def - node @member.typeof -} - -@struct [StructDefinition [StructMembers - @member item: [StructMember @type_name [TypeName] @name name: [Identifier]] -]] { - attr (@member.def) node_definition = @name - attr (@member.def) definiens_node = @member - - edge @struct.members -> @member.def - - edge @type_name.type_ref -> @struct.lexical_scope - - attr (@member.typeof) push_symbol = "@typeof" - - edge @member.def -> @member.typeof - edge @member.typeof -> @type_name.output -} - -@struct [StructDefinition [StructMembers . @first_member [StructMember]]] { - ; As a public getter result, the value returned is a tuple with all our fields flattened - ; We only care about the first member for name binding, since tuples are not real types - node getter_call - attr (getter_call) pop_symbol = "@as_getter" - edge @struct.typeof -> getter_call - edge getter_call -> @first_member.typeof -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Event definitions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@event [EventDefinition @name name: [Identifier]] { - node @event.lexical_scope - node @event.def - - attr (@event.def) node_definition = @name - attr (@event.def) definiens_node = @event - - node @event.params - attr (@event.params) pop_symbol = "@param_names" - edge @event.def -> @event.params - - ; Bind to built-in %EventType for accessing built-in member `.selector` - node typeof - attr (typeof) push_symbol = "@typeof" - node event_type - attr (event_type) push_symbol = "%EventType" - edge @event.def -> typeof - edge typeof -> event_type - edge event_type -> @event.lexical_scope -} - -@event [EventDefinition [EventParametersDeclaration [EventParameters - [EventParameter @type_name type_name: [TypeName]] -]]] { - edge @type_name.type_ref -> @event.lexical_scope -} - -@event [EventDefinition [EventParametersDeclaration [EventParameters - @param [EventParameter - @name name: [Identifier] - ] -]]] { - node def - attr (def) node_definition = @name - attr (def) definiens_node = @param - - edge @event.params -> def -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Error definitions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@error [ErrorDefinition @name name: [Identifier]] { - node @error.lexical_scope - node @error.def - - attr (@error.def) node_definition = @name - attr (@error.def) definiens_node = @error - - node @error.params - attr (@error.params) pop_symbol = "@param_names" - edge @error.def -> @error.params - - ; Bind to built-in errorType for accessing built-in member `.selector` - node typeof - attr (typeof) push_symbol = "@typeof" - node error_type - attr (error_type) push_symbol = "%ErrorType" - edge @error.def -> typeof - edge typeof -> error_type - edge error_type -> @error.lexical_scope -} - -@error [ErrorDefinition [ErrorParametersDeclaration [ErrorParameters - [ErrorParameter @type_name type_name: [TypeName]] -]]] { - edge @type_name.type_ref -> @error.lexical_scope -} - -@error [ErrorDefinition [ErrorParametersDeclaration [ErrorParameters - @param [ErrorParameter - @name name: [Identifier] - ] -]]] { - node def - attr (def) node_definition = @name - attr (def) definiens_node = @param - - edge @error.params -> def -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Other named definitions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -@constant [ConstantDefinition] { - node @constant.lexical_scope - node @constant.def -} - -@constant [ConstantDefinition - @type_name type_name: [TypeName] - @name name: [Identifier] -] { - node def - attr (def) node_definition = @name - attr (def) definiens_node = @constant - - edge @constant.def -> def - - node typeof - attr (typeof) push_symbol = "@typeof" - - edge def -> typeof - edge typeof -> @type_name.output - edge @type_name.type_ref -> @constant.lexical_scope -} - -@user_type [UserDefinedValueTypeDefinition @name [Identifier] @value_type [ElementaryType]] { - node @user_type.lexical_scope - node @user_type.def - - attr (@user_type.def) node_definition = @name - attr (@user_type.def) definiens_node = @user_type - - ; Provide member resolution through the built-in `%userTypeType` - ; Because the built-in is defined as a struct, we need to push an extra `@typeof` - node member_guard - attr (member_guard) pop_symbol = "." - node member - attr (member) push_symbol = "." - node typeof - attr (typeof) push_symbol = "@typeof" - node user_type_type - attr (user_type_type) push_symbol = "%UserDefinedValueType" - - edge @user_type.def -> member_guard - edge member_guard -> member - edge member -> typeof - edge typeof -> user_type_type - edge user_type_type -> @user_type.lexical_scope - - ; Hard-code built-in functions `wrap` and `unwrap` in order to be able to - ; resolve their return types - node wrap - attr (wrap) pop_symbol = "wrap" - node wrap_call - attr (wrap_call) pop_symbol = "()" - node wrap_typeof - attr (wrap_typeof) push_symbol = "@typeof" - node type_ref - attr (type_ref) push_symbol = (source-text @name) - - edge member_guard -> wrap - edge wrap -> wrap_call - edge wrap_call -> wrap_typeof - edge wrap_typeof -> type_ref - edge type_ref -> @user_type.lexical_scope - - node unwrap - attr (unwrap) pop_symbol = "unwrap" - node unwrap_call - attr (unwrap_call) pop_symbol = "()" - node unwrap_typeof - attr (unwrap_typeof) push_symbol = "@typeof" - - edge member_guard -> unwrap - edge unwrap -> unwrap_call - edge unwrap_call -> unwrap_typeof - edge unwrap_typeof -> @value_type.ref - edge @value_type.ref -> @user_type.lexical_scope -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Expressions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Expressions have two important scoped variables: -;; - @expr.lexical_scope should be set by the enclosing node to provide a scope -;; for name resolution -;; - @expr.output is a node provided by the expression and represents the output -;; of the expression for chaining eg. with a member access - -@expr [Expression] { - ;; this is an output scope for use in member access (and other uses) - node @expr.output -} - -;; Identifier expressions -@expr [Expression @name [Identifier]] { - node ref - attr (ref) node_reference = @name - attr (ref) parents = [@expr.enclosing_def] - - edge ref -> @expr.lexical_scope - edge @expr.output -> ref -} - -@expr [Expression @keyword ([ThisKeyword] | [SuperKeyword])] { - ; This is almost equivalent to the above rule, except it doesn't generate a reference - node keyword - attr (keyword) push_symbol = (source-text @keyword) - edge keyword -> @expr.lexical_scope - edge @expr.output -> keyword -} - -;; Member access expressions -@expr [Expression [MemberAccessExpression - @operand operand: [Expression] - @name member: [Identifier] -]] { - node @name.ref - attr (@name.ref) node_reference = @name - attr (@name.ref) parents = [@expr.enclosing_def] - - node member - attr (member) push_symbol = "." - - edge @name.ref -> member - edge member -> @operand.output - - edge @expr.output -> @name.ref - - ; Shortcut path for expressions inside contracts with using X for * directives - edge member -> @expr.star_extension -} - -;; Elementary types used as expressions (eg. for type casting, or for built-ins like `string.concat`) -@expr [Expression @type [ElementaryType]] { - edge @expr.output -> @type.ref - edge @type.ref -> @expr.lexical_scope - - ; Elementary types can also be used for casting; instead of defining built-in - ; struct for each available elementary type, we define a special path here - node call - attr (call) pop_symbol = "()" - node typeof - attr (typeof) push_symbol = "@typeof" - edge @expr.output -> call - edge call -> typeof - edge typeof -> @type.ref -} - -;; Index access expressions -@expr [Expression [IndexAccessExpression - @operand operand: [Expression] -]] { - node index - attr (index) push_symbol = "[]" - - edge @expr.output -> index - edge index -> @operand.output -} - -;; Type expressions -@type_expr [Expression [TypeExpression @type [TypeName]]] { - edge @type.type_ref -> @type_expr.lexical_scope -} - -@type_expr [Expression [TypeExpression - @type [TypeName [ElementaryType ([IntKeyword] | [UintKeyword])]] -]] { - ; For integer types the type's type is fixed - node typeof - attr (typeof) push_symbol = "@typeof" - node type - attr (type) push_symbol = "%IntTypeType" - - edge @type_expr.output -> typeof - edge typeof -> type - edge type -> @type_expr.lexical_scope - - ; Resolve the type of min() and max() to the operand's type - if (version-matches ">= 0.6.8") { - node built_in_member - attr (built_in_member) pop_symbol = "." - node min_built_in - attr (min_built_in) pop_symbol = "min" - node max_built_in - attr (max_built_in) pop_symbol = "max" - node typeof_builtin - attr (typeof_builtin) push_symbol = "@typeof" - - edge @type_expr.output -> built_in_member - edge built_in_member -> min_built_in - edge built_in_member -> max_built_in - edge min_built_in -> typeof_builtin - edge max_built_in -> typeof_builtin - edge typeof_builtin -> @type.output - } -} - -@type_expr [Expression [TypeExpression [TypeName @id_path [IdentifierPath]]]] { - ; For other identifiers, resolve it through a pseudo-member `%type` - node typeof - attr (typeof) push_symbol = "@typeof" - node type - attr (type) push_symbol = "@type" - - edge @type_expr.output -> typeof - edge typeof -> type - edge type -> @id_path.push_begin -} - -;; New expressions - -@new_expr [Expression [NewExpression @type [TypeName]]] { - edge @type.type_ref -> @new_expr.lexical_scope - edge @new_expr.output -> @type.output - if (version-matches "< 0.7.0") { - ;; Expose a function interface to be able to use legacy call options on new expressions - node function - attr (function) push_symbol = "%ExternalFunction" - node typeof - attr (typeof) push_symbol = "@typeof" - edge @new_expr.output -> typeof - edge typeof -> function - edge function -> @new_expr.lexical_scope - } -} - - -;;; Function call expressions - -@args [ArgumentsDeclaration] { - node @args.lexical_scope - - node @args.refs - attr (@args.refs) push_symbol = "@param_names" -} - -@named_arg [NamedArgument @name [Identifier] [Colon] [Expression]] { - node @named_arg.lexical_scope - - node @named_arg.ref - attr (@named_arg.ref) node_reference = @name - ;; Needed if the function call is for an extension function - attr (@named_arg.ref) parents = [@named_arg.enclosing_def] -} - -@args [ArgumentsDeclaration [NamedArgumentsDeclaration - [NamedArgumentGroup [NamedArguments @argument [NamedArgument]]] -]] { - edge @argument.lexical_scope -> @args.lexical_scope - edge @argument.ref -> @args.refs -} - -@funcall [Expression [FunctionCallExpression - @operand [Expression] - @args [ArgumentsDeclaration] -]] { - edge @args.lexical_scope -> @funcall.lexical_scope - - ;; Connect to the output of the function name to be able to resolve named arguments - edge @args.refs -> @operand.output - - node call - attr (call) push_symbol = "()" - - edge @funcall.output -> call - edge call -> @operand.output -} - - -;;; Call options - -@expr [Expression [CallOptionsExpression @operand [Expression] @options [CallOptions]]] { - edge @expr.output -> @operand.output - - node @options.refs - attr (@options.refs) push_symbol = "." - - node typeof - attr (typeof) push_symbol = "@typeof" - - node call_options - attr (call_options) push_symbol = "%CallOptions" - - edge @options.refs -> typeof - edge typeof -> call_options - edge call_options -> @expr.lexical_scope -} - -@expr [Expression [CallOptionsExpression - @options [CallOptions @named_arg [NamedArgument]] -]] { - edge @named_arg.lexical_scope -> @expr.lexical_scope - edge @named_arg.ref -> @options.refs -} - - -;;; Payable -; These work like `address`, should they should bind to `address` -@expr [Expression [PayableKeyword]] { - node ref - attr (ref) push_symbol = "address payable" - - edge ref -> @expr.lexical_scope - edge @expr.output -> ref -} - - -;;; Tuple expressions - -; Parenthesized expressions are parsed as tuples of a single value -@expr [Expression [TupleExpression [TupleValues . [TupleValue @operand [Expression]] .]]] { - edge @expr.output -> @operand.output -} - -;;; Arithmetic, bitwise & logical operators, etc - -; Bind to the left operand only: assignment expressions -@expr [Expression [_ - @left_operand left_operand: [Expression] - ( - [Equal] - | [BarEqual] - | [PlusEqual] - | [MinusEqual] - | [CaretEqual] - | [SlashEqual] - | [PercentEqual] - | [AsteriskEqual] - | [AmpersandEqual] - | [LessThanLessThanEqual] - | [GreaterThanGreaterThanEqual] - | [GreaterThanGreaterThanGreaterThanEqual] - ) -]] { - edge @expr.output -> @left_operand.output -} - -; Unary operators postfix -@expr [Expression [_ - @operand operand: [Expression] - ([PlusPlus] | [MinusMinus]) -]] { - edge @expr.output -> @operand.output -} - -; Unary operators prefix -@expr [Expression [_ - ([PlusPlus] | [MinusMinus] | [Tilde] | [Bang] | [Minus] | [Plus]) - @operand operand: [Expression] -]] { - edge @expr.output -> @operand.output -} - -; Bind to both operands: logical and/or, arithmetic, bit-wise expressions -@expr [Expression [_ - @left_operand left_operand: [Expression] - ( - [BarBar] - | [AmpersandAmpersand] - - | [Plus] - | [Minus] - | [Asterisk] - | [Slash] - | [Percent] - | [AsteriskAsterisk] - - | [Bar] - | [Caret] - | [Ampersand] - - | [LessThanLessThan] - | [GreaterThanGreaterThan] - | [GreaterThanGreaterThanGreaterThan] - ) - @right_operand right_operand: [Expression] -]] { - edge @expr.output -> @left_operand.output - edge @expr.output -> @right_operand.output -} - -; Comparison operators bind to bool type -@expr [Expression [_ - ( - [EqualEqual] - | [BangEqual] - | [LessThan] - | [GreaterThan] - | [LessThanEqual] - | [GreaterThanEqual] - ) -]] { - node typeof - attr (typeof) push_symbol = "@typeof" - node bool - attr (bool) push_symbol = "bool" - edge @expr.output -> typeof - edge typeof -> bool - edge bool -> @expr.lexical_scope -} - -; Ternary conditional expression binds to both branches -@expr [Expression [ConditionalExpression - @true_expression true_expression: [Expression] - @false_expression false_expression: [Expression] -]] { - edge @expr.output -> @true_expression.output - edge @expr.output -> @false_expression.output -} - - -;;; Literal Decimal expressions -;;; These are treated as uint256 by default, even though in practice they are -;;; rationals and are implicitly cast to any integer type -@expr [Expression [DecimalNumberExpression]] { - node typeof - attr (typeof) push_symbol = "@typeof" - node address - attr (address) push_symbol = "uint256" - - edge @expr.output -> typeof - edge typeof -> address - edge address -> @expr.lexical_scope -} - -;;; Literal Address Expressions -@expr [Expression [HexNumberExpression @hex_literal [HexLiteral]]] { - scan (source-text @hex_literal) { - "0x[0-9a-fA-F]{40}" { - ; Treat it as a valid address - node typeof - attr (typeof) push_symbol = "@typeof" - node address - attr (address) push_symbol = "address" - edge @expr.output -> typeof - edge typeof -> address - edge address -> @expr.lexical_scope - } - } -} - - -;;; Literal array expressions -@expr [Expression [ArrayExpression [ArrayValues . @item [Expression]]]] { - node typeof - attr (typeof) push_symbol = "@typeof" - node fixed_array - attr (fixed_array) push_symbol = "%FixedArray" - - edge @expr.output -> typeof - edge typeof -> fixed_array - edge fixed_array -> @expr.lexical_scope - - ; add the type of the first element to the %FixedArray type - edge fixed_array -> @item.output -} - - -;;; Literal booleans -@expr [Expression ([TrueKeyword] | [FalseKeyword])] { - node typeof - attr (typeof) push_symbol = "@typeof" - node bool - attr (bool) push_symbol = "bool" - - edge @expr.output -> typeof - edge typeof -> bool - edge bool -> @expr.lexical_scope -} - - -;;; String expressions -@expr [Expression [StringExpression]] { - node typeof - attr (typeof) push_symbol = "@typeof" - node string - attr (string) push_symbol = "string" - - edge @expr.output -> typeof - edge typeof -> string - edge string -> @expr.lexical_scope -} - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Yul -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;; Blocks and statements - -@block [YulBlock] { - node @block.lexical_scope - ; Variables defined in this block (only used to forward the init block - ; declarations in a for statement) - node @block.variable_defs - ; Function definitions accessible from the block (ie. defined in the block, or - ; accessible in the enclosing parent block) - node @block.function_defs - - edge @block.lexical_scope -> @block.function_defs -} - -@block [YulBlock [YulStatements . @stmt [YulStatement]]] { - edge @stmt.lexical_scope -> @block.lexical_scope -} - -@block [YulBlock [YulStatements @stmt [YulStatement]]] { - edge @stmt.function_scope -> @block.function_defs - edge @block.variable_defs -> @stmt.defs -} - -[YulStatements @left_stmt [YulStatement] . @right_stmt [YulStatement]] { - edge @right_stmt.lexical_scope -> @left_stmt.lexical_scope - ; variable declaration are accessible from the next statement - edge @right_stmt.lexical_scope -> @left_stmt.defs -} - -@stmt [YulStatement] { - node @stmt.lexical_scope - node @stmt.defs - ;; Functions visible in this scope (to propagate to inner function - ;; definitions, since the lexical scope is not accessible inside a function - ;; body) - node @stmt.function_scope -} - -;;; Blocks as statements - -@stmt [YulStatement @block variant: [YulBlock]] { - edge @block.lexical_scope -> @stmt.lexical_scope - edge @block.function_defs -> @stmt.function_scope -} - -;;; Expression as statements - -@stmt [YulStatement @expr_stmt [YulExpression]] { - edge @expr_stmt.lexical_scope -> @stmt.lexical_scope -} - -;;; Variable declarations - -@stmt [YulStatement @var_decl [YulVariableDeclarationStatement]] { - edge @var_decl.lexical_scope -> @stmt.lexical_scope - edge @stmt.defs -> @var_decl.defs -} - -@var_decl [YulVariableDeclarationStatement] { - node @var_decl.lexical_scope - node @var_decl.defs -} - -@var_decl [YulVariableDeclarationStatement [YulVariableNames @name [YulIdentifier]]] { - node def - attr (def) node_definition = @name - attr (def) definiens_node = @name - - edge @var_decl.defs -> def -} - -@var_decl [YulVariableDeclarationStatement [YulVariableDeclarationValue - @value [YulExpression] -]] { - edge @value.lexical_scope -> @var_decl.lexical_scope -} - -;;; Variable assignments - -@stmt [YulStatement @var_assign [YulVariableAssignmentStatement]] { - edge @var_assign.lexical_scope -> @stmt.lexical_scope -} - -@var_assign [YulVariableAssignmentStatement] { - node @var_assign.lexical_scope -} - -@var_assign [YulVariableAssignmentStatement [YulPaths @path [YulPath]]] { - edge @path.lexical_scope -> @var_assign.lexical_scope -} - -@var_assign [YulVariableAssignmentStatement @expr expression: [YulExpression]] { - edge @expr.lexical_scope -> @var_assign.lexical_scope -} - -;;; Function definitions - -@block [YulBlock [YulStatements [YulStatement @fundef [YulFunctionDefinition]]]] { - ;; Function definitions are hoisted in the enclosing block - edge @block.function_defs -> @fundef.def - ;; The only definitions available in the function's lexical scope (other than - ;; parameters) are functions (ie. the body of the function doesn't have access - ;; to any outside variables) - edge @fundef.lexical_scope -> @block.function_defs - ; Exception: but constants and built-ins *are* available, so we provide a - ; guarded access to the parent lexical scope. This guard will be popped at the - ; assembly statement to link to constants and built-ins. - node yul_function_guard - attr (yul_function_guard) push_symbol = "@in_yul_function" - node yul_function_pop - attr (yul_function_pop) pop_symbol = "@in_yul_function" - edge @fundef.lexical_scope -> yul_function_guard - edge @fundef.lexical_scope -> yul_function_pop - ; pops an existing guard to avoid duplicating it if we're resolving from a - ; nested Yul function - edge yul_function_pop -> yul_function_guard - edge yul_function_guard -> @block.lexical_scope -} - -;; Constants need to be available in Yul assembly blocks. We create a scope in -;; the source unit, contracts and libraries, and guard it from the lexical -;; scope, so we can link constant definitions here. This uses the same guard as -;; Yul built-ins (see __SLANG_SOLIDITY_YUL_BUILT_INS_GUARD__) so constants are -;; in an equivalent scope to predefined built-ins. See the dual path in the rule -;; above. -@source_unit [SourceUnit] { - node @source_unit.yul_globals_guarded_scope - attr (@source_unit.yul_globals_guarded_scope) pop_symbol = "@yul" - ;; export the Yul specific scope - edge @source_unit.defs -> @source_unit.yul_globals_guarded_scope - ;; and provide it locally - edge @source_unit.lexical_scope -> @source_unit.yul_globals_guarded_scope -} - -@contract_or_library ([ContractDefinition] | [LibraryDefinition]) { - node @contract_or_library.yul_globals_guarded_scope - attr (@contract_or_library.yul_globals_guarded_scope) pop_symbol = "@yul" - edge @contract_or_library.ns -> @contract_or_library.yul_globals_guarded_scope -} - -;; Make top-level constants available inside Yul functions -@source_unit [SourceUnit [SourceUnitMembers [SourceUnitMember @constant [ConstantDefinition]]]] { - edge @source_unit.yul_globals_guarded_scope -> @constant.def -} - -;; Ditto for contracts, interfaces and libraries -@contract [_ members: [_ [ContractMember - @constant [StateVariableDefinition - [StateVariableAttributes [StateVariableAttribute [ConstantKeyword]]] - ] -]]] { - edge @contract.yul_globals_guarded_scope -> @constant.def -} - -@fundef [YulFunctionDefinition - @name name: [YulIdentifier] - @body body: [YulBlock] -] { - node @fundef.lexical_scope - node @fundef.def - - node def - attr (def) node_definition = @name - attr (def) definiens_node = @fundef - - edge @fundef.def -> def - edge @body.lexical_scope -> @fundef.lexical_scope -} - -@fundef [YulFunctionDefinition [YulParametersDeclaration [YulParameters - @param [YulIdentifier] -]]] { - node def - attr (def) node_definition = @param - attr (def) definiens_node = @param - - edge @fundef.lexical_scope -> def -} - -@fundef [YulFunctionDefinition [YulReturnsDeclaration [YulVariableNames - @return_param [YulIdentifier] -]]] { - node def - attr (def) node_definition = @return_param - attr (def) definiens_node = @return_param - - edge @fundef.lexical_scope -> def -} - -;;; Stack assignment (Solidity < 0.5.0) - -@stmt [YulStatement [YulStackAssignmentStatement @name [YulIdentifier]]] { - node ref - attr (ref) node_reference = @name - - edge ref -> @stmt.lexical_scope -} - -;;; If statements - -@stmt [YulStatement [YulIfStatement - @condition condition: [YulExpression] - @body body: [YulBlock] -]] { - edge @condition.lexical_scope -> @stmt.lexical_scope - edge @body.lexical_scope -> @stmt.lexical_scope - edge @body.function_defs -> @stmt.function_scope -} - -;;; Switch statements - -@stmt [YulStatement [YulSwitchStatement - @expr expression: [YulExpression] -]] { - edge @expr.lexical_scope -> @stmt.lexical_scope -} - -@stmt [YulStatement [YulSwitchStatement [YulSwitchCases [YulSwitchCase - [_ @body body: [YulBlock]] -]]]] { - edge @body.lexical_scope -> @stmt.lexical_scope - edge @body.function_defs -> @stmt.function_scope -} - -;;; For statements - -@stmt [YulStatement [YulForStatement - @init initialization: [YulBlock] - @cond condition: [YulExpression] - @iter iterator: [YulBlock] - @body body: [YulBlock] -]] { - edge @init.lexical_scope -> @stmt.lexical_scope - edge @cond.lexical_scope -> @stmt.lexical_scope - edge @iter.lexical_scope -> @stmt.lexical_scope - edge @body.lexical_scope -> @stmt.lexical_scope - - edge @cond.lexical_scope -> @init.variable_defs - edge @iter.lexical_scope -> @init.variable_defs - edge @body.lexical_scope -> @init.variable_defs -} - -;;; Label statements (Solidity < 0.5.0) - -@block [YulBlock [YulStatements [YulStatement @label [YulLabel @name label: [YulIdentifier]]]]] { - node def - attr (def) node_definition = @name - attr (def) definiens_node = @label - - ; Labels are hoisted to the beginning of the block - edge @block.lexical_scope -> def -} - -;;; Expressions - -@expr [YulExpression] { - node @expr.lexical_scope -} - -@expr [YulExpression @path [YulPath]] { - edge @path.lexical_scope -> @expr.lexical_scope -} - -@path [YulPath] { - node @path.lexical_scope -} - -@path [YulPath . @name [YulIdentifier]] { - node ref - - ; Handle `byte` and `return` as special cases, since we cannot define them - ; directly as regular built-ins - var yul_symbol = (source-text @name) - attr (ref) symbol_reference = yul_symbol - attr (ref) source_node = @name - - edge ref -> @path.lexical_scope - - if (version-matches "< 0.7.0") { - ; Before Solidity 0.7.0 storage variables' `.offset` and `.slot` were - ; accessed by suffixing the name with `_offset` and `_slot` - scan (source-text @name) { - "^(.*)_(slot|offset|length)$" { - let symbol = $0 - let without_suffix = $1 - let suffix = $2 - - ; We bind the whole symbol to the built-in field for the known cases - node pop_ref - attr (pop_ref) pop_symbol = symbol - node push_suffixless - attr (push_suffixless) push_symbol = suffix - node member_of - attr (member_of) push_symbol = "." - node typeof - attr (typeof) push_symbol = "@typeof" - node yul_external - attr (yul_external) push_symbol = "%YulExternal" - - edge ref -> pop_ref - edge pop_ref -> push_suffixless - edge push_suffixless -> member_of - edge member_of -> typeof - edge typeof -> yul_external - edge yul_external -> @path.lexical_scope - } - } - } -} - -@path [YulPath [Period] @member [YulIdentifier] .] { - ; Yul variable members only apply to external variables and hence are - ; automatically bound to a special %YulExternal built-in - node ref - attr (ref) node_reference = @member - node member_of - attr (member_of) push_symbol = "." - node typeof - attr (typeof) push_symbol = "@typeof" - node yul_external - attr (yul_external) push_symbol = "%YulExternal" - - edge ref -> member_of - edge member_of -> typeof - edge typeof -> yul_external - edge yul_external -> @path.lexical_scope -} - -@expr [YulExpression @funcall [YulFunctionCallExpression]] { - edge @funcall.lexical_scope -> @expr.lexical_scope -} - -@funcall [YulFunctionCallExpression - @operand operand: [YulExpression] - @args arguments: [YulArguments] -] { - node @funcall.lexical_scope - - edge @operand.lexical_scope -> @funcall.lexical_scope - edge @args.lexical_scope -> @funcall.lexical_scope -} - -@args [YulArguments] { - node @args.lexical_scope -} - -@args [YulArguments @arg [YulExpression]] { - edge @arg.lexical_scope -> @args.lexical_scope -} diff --git a/crates/solidity-v2/inputs/language/src/definition.rs b/crates/solidity-v2/inputs/language/src/definition.rs index ec2ab1ad26..f43b9886e7 100644 --- a/crates/solidity-v2/inputs/language/src/definition.rs +++ b/crates/solidity-v2/inputs/language/src/definition.rs @@ -2,7 +2,6 @@ pub use solidity::SolidityDefinition; language_v2_macros::compile!(Language( name = Solidity, - binding_rules_file = "crates/solidity/inputs/language/bindings/rules.msgb", root_item = SourceUnit, leading_trivia = OneOrMore(Choice([ Trivia(Whitespace), diff --git a/crates/solidity-v2/outputs/cargo/parser/src/lexer/contexts.rs.jinja2 b/crates/solidity-v2/outputs/cargo/parser/src/lexer/contexts.rs.jinja2 index bbebfb49c8..5132d556de 100644 --- a/crates/solidity-v2/outputs/cargo/parser/src/lexer/contexts.rs.jinja2 +++ b/crates/solidity-v2/outputs/cargo/parser/src/lexer/contexts.rs.jinja2 @@ -96,20 +96,18 @@ impl<'source> ContextWrapper<'source> { {%- elif lexeme.type == "Keyword" -%} #[regex(r#"{{ lexeme.regex }}"#, {{- " " -}} - {%- if lexeme.reserved.type -%} - {%- if lexeme.reserved.type == "Never" -%} - |_| { LexemeKind::{{ lexeme.kind }}_Unreserved } - {%- elif lexeme.reserved.type == "From" -%} - |lexer| { if LanguageVersion::V{{ lexeme.reserved.from | split(pat=".") | join(sep="_") }} <= lexer.extras.language_version { LexemeKind::{{ lexeme.kind }}_Reserved } else { LexemeKind::{{ lexeme.kind }}_Unreserved } } - {%- elif lexeme.reserved.type == "Till" -%} - |lexer| { if lexer.extras.language_version < LanguageVersion::V{{ lexeme.reserved.till | split(pat=".") | join(sep="_") }} { LexemeKind::{{ lexeme.kind }}_Reserved } else { LexemeKind::{{ lexeme.kind }}_Unreserved } } - {%- elif lexeme.reserved.type == "Range" -%} - |lexer| { if LanguageVersion::V{{ lexeme.reserved.from | split(pat=".") | join(sep="_") }} <= lexer.extras.language_version && lexer.extras.language_version < LanguageVersion::V{{ lexeme.reserved.till | split(pat=".") | join(sep="_") }} { LexemeKind::{{ lexeme.kind }}_Reserved } else { LexemeKind::{{ lexeme.kind }}_Unreserved } } - {%- else -%} - {{ throw(message = "Unrecognized lexeme.reserved type: " ~ lexeme.reserved.type) }} - {%- endif -%} - {%- else -%} + {%- if lexeme.reserved.type == "Always" -%} |_| { LexemeKind::{{ lexeme.kind }}_Reserved } + {%- elif lexeme.reserved.type == "Never" -%} + |_| { LexemeKind::{{ lexeme.kind }}_Unreserved } + {%- elif lexeme.reserved.type == "From" -%} + |lexer| { if LanguageVersion::V{{ lexeme.reserved.from | split(pat=".") | join(sep="_") }} <= lexer.extras.language_version { LexemeKind::{{ lexeme.kind }}_Reserved } else { LexemeKind::{{ lexeme.kind }}_Unreserved } } + {%- elif lexeme.reserved.type == "Till" -%} + |lexer| { if lexer.extras.language_version < LanguageVersion::V{{ lexeme.reserved.till | split(pat=".") | join(sep="_") }} { LexemeKind::{{ lexeme.kind }}_Reserved } else { LexemeKind::{{ lexeme.kind }}_Unreserved } } + {%- elif lexeme.reserved.type == "Range" -%} + |lexer| { if LanguageVersion::V{{ lexeme.reserved.from | split(pat=".") | join(sep="_") }} <= lexer.extras.language_version && lexer.extras.language_version < LanguageVersion::V{{ lexeme.reserved.till | split(pat=".") | join(sep="_") }} { LexemeKind::{{ lexeme.kind }}_Reserved } else { LexemeKind::{{ lexeme.kind }}_Unreserved } } + {%- else -%} + {{ throw(message = "Unrecognized lexeme.reserved type: " ~ lexeme.reserved.type) }} {%- endif -%} , priority = {{ 3000000 + loop.index }})] {%- else -%}