소스 검색

Parse override(list) syntax on variable definition

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 3 년 전
부모
커밋
e22e008d3f

+ 2 - 2
solang-parser/src/pt.rs

@@ -326,7 +326,7 @@ pub enum VariableAttribute {
     Visibility(Visibility),
     Constant(Loc),
     Immutable(Loc),
-    Override(Loc),
+    Override(Loc, Vec<IdentifierPath>),
 }
 
 #[derive(Debug, PartialEq, Clone)]
@@ -595,7 +595,7 @@ pub enum FunctionAttribute {
     Visibility(Visibility),
     Virtual(Loc),
     Immutable(Loc),
-    Override(Loc, Vec<Identifier>),
+    Override(Loc, Vec<IdentifierPath>),
     BaseOrModifier(Loc, Base),
 }
 

+ 7 - 2
solang-parser/src/solidity.lalrpop

@@ -265,7 +265,10 @@ VariableAttribute: VariableAttribute = {
     Visibility => VariableAttribute::Visibility(<>),
     <l:@L> "constant" <r:@R> => VariableAttribute::Constant(Loc::File(file_no, l, r)),
     <l:@L> "immutable" <r:@R> => VariableAttribute::Immutable(Loc::File(file_no, l, r)),
-    <l:@L> "override" <r:@R> => VariableAttribute::Override(Loc::File(file_no, l, r))
+    <l:@L> "override" <r:@R> => VariableAttribute::Override(Loc::File(file_no, l, r), Vec::new()),
+    <l:@L> "override" "(" <list:CommaOne<SolIdentifierPath>> ")" <r:@R> => {
+        VariableAttribute::Override(Loc::File(file_no, l, r), list)
+    }
 }
 
 Expression: Expression = {
@@ -514,7 +517,9 @@ FunctionAttribute: FunctionAttribute = {
     <l:@L> "immutable" <r:@R> => FunctionAttribute::Immutable(Loc::File(file_no, l, r)),
     <l:@L> "virtual" <r:@R> => FunctionAttribute::Virtual(Loc::File(file_no, <>)),
     <l:@L> "override" <r:@R> => FunctionAttribute::Override(Loc::File(file_no, <>), Vec::new()),
-    <l:@L> "override" "(" <list:CommaOne<SolIdentifier>> ")" <r:@R> => FunctionAttribute::Override(Loc::File(file_no, l, r), list),
+    <l:@L> "override" "(" <list:CommaOne<SolIdentifierPath>> ")" <r:@R> => {
+        FunctionAttribute::Override(Loc::File(file_no, l, r), list)
+    },
     <l:@L> <base:Base> <r:@R> => FunctionAttribute::BaseOrModifier(Loc::File(file_no, l, r), base),
 }
 

+ 1 - 1
src/sema/contracts.rs

@@ -513,7 +513,7 @@ fn check_inheritance(contract_no: usize, ns: &mut ast::Namespace) {
                 if previous_defs.is_empty() && cur.is_override.is_some() {
                     ns.diagnostics.push(ast::Diagnostic::error(
                         cur.loc,
-                        format!("function '{}' does not override anything", cur.name),
+                        format!("'{}' does not override anything", cur.name),
                     ));
                     continue;
                 }

+ 19 - 22
src/sema/functions.rs

@@ -177,36 +177,33 @@ pub fn contract_function(
                 }
 
                 let mut list = Vec::new();
+                let mut diagnostics = Vec::new();
 
                 for name in bases {
-                    match ns.resolve_contract(file_no, name) {
-                        Some(no) => {
-                            if list.contains(&no) {
-                                ns.diagnostics.push(Diagnostic::error(
-                                    name.loc,
-                                    format!("function duplicate override '{}'", name.name),
-                                ));
-                            } else if !is_base(no, contract_no, ns) {
-                                ns.diagnostics.push(Diagnostic::error(
-                                    name.loc,
-                                    format!(
-                                        "override '{}' is not a base contract of '{}'",
-                                        name.name, ns.contracts[contract_no].name
-                                    ),
-                                ));
-                            } else {
-                                list.push(no);
-                            }
-                        }
-                        None => {
-                            ns.diagnostics.push(Diagnostic::error(
+                    if let Ok(no) =
+                        ns.resolve_contract_with_namespace(file_no, name, &mut diagnostics)
+                    {
+                        if list.contains(&no) {
+                            diagnostics.push(Diagnostic::error(
                                 name.loc,
-                                format!("contract '{}' in override list not found", name.name),
+                                format!("function duplicate override '{}'", name),
                             ));
+                        } else if !is_base(no, contract_no, ns) {
+                            diagnostics.push(Diagnostic::error(
+                                name.loc,
+                                format!(
+                                    "override '{}' is not a base contract of '{}'",
+                                    name, ns.contracts[contract_no].name
+                                ),
+                            ));
+                        } else {
+                            list.push(no);
                         }
                     }
                 }
 
+                ns.diagnostics.extend(diagnostics);
+
                 is_override = Some((*loc, list));
             }
             pt::FunctionAttribute::BaseOrModifier(loc, _) => {

+ 44 - 7
src/sema/variables.rs

@@ -3,6 +3,7 @@ use super::{
         BuiltinStruct, Diagnostic, Expression, Function, Namespace, Parameter, Statement, Symbol,
         Type, Variable,
     },
+    contracts::is_base,
     expression::{expression, ExprContext, ResolveTo},
     symtable::Symtable,
     symtable::{VariableInitializer, VariableUsage},
@@ -125,7 +126,7 @@ pub fn variable_decl<'a>(
     let mut constant = false;
     let mut visibility: Option<pt::Visibility> = None;
     let mut has_immutable: Option<pt::Loc> = None;
-    let mut has_override: Option<pt::Loc> = None;
+    let mut is_override: Option<(pt::Loc, Vec<usize>)> = None;
 
     for attr in attrs {
         match &attr {
@@ -149,8 +150,8 @@ pub fn variable_decl<'a>(
                 }
                 has_immutable = Some(*loc);
             }
-            pt::VariableAttribute::Override(loc) => {
-                if let Some(prev) = &has_override {
+            pt::VariableAttribute::Override(loc, bases) => {
+                if let Some((prev, _)) = &is_override {
                     ns.diagnostics.push(Diagnostic::error_with_note(
                         *loc,
                         "duplicate 'override' attribute".to_string(),
@@ -158,7 +159,43 @@ pub fn variable_decl<'a>(
                         "previous 'override' attribute".to_string(),
                     ));
                 }
-                has_override = Some(*loc);
+
+                let mut list = Vec::new();
+                let mut diagnostics = Vec::new();
+
+                if let Some(contract_no) = contract_no {
+                    for name in bases {
+                        if let Ok(no) =
+                            ns.resolve_contract_with_namespace(file_no, name, &mut diagnostics)
+                        {
+                            if list.contains(&no) {
+                                diagnostics.push(Diagnostic::error(
+                                    name.loc,
+                                    format!("duplicate override '{}'", name),
+                                ));
+                            } else if !is_base(no, contract_no, ns) {
+                                diagnostics.push(Diagnostic::error(
+                                    name.loc,
+                                    format!(
+                                        "override '{}' is not a base contract of '{}'",
+                                        name, ns.contracts[contract_no].name
+                                    ),
+                                ));
+                            } else {
+                                list.push(no);
+                            }
+                        }
+                    }
+
+                    is_override = Some((*loc, list));
+                } else {
+                    diagnostics.push(Diagnostic::error(
+                        *loc,
+                        "global variable has no bases contracts to override".to_string(),
+                    ));
+                }
+
+                ns.diagnostics.extend(diagnostics);
             }
             pt::VariableAttribute::Visibility(v) if contract_no.is_none() => {
                 ns.diagnostics.push(Diagnostic::error(
@@ -207,12 +244,12 @@ pub fn variable_decl<'a>(
 
     if let pt::Visibility::Public(_) = &visibility {
         // override allowed
-    } else if let Some(loc) = &has_override {
+    } else if let Some((loc, _)) = &is_override {
         ns.diagnostics.push(Diagnostic::error(
             *loc,
             "only public variable can be declared 'override'".to_string(),
         ));
-        has_override = None;
+        is_override = None;
     }
 
     if let Some(contract) = contract {
@@ -452,7 +489,7 @@ pub fn variable_decl<'a>(
             )];
             func.is_accessor = true;
             func.has_body = true;
-            func.is_override = has_override.map(|loc| (loc, Vec::new()));
+            func.is_override = is_override;
             func.symtable = symtable;
 
             // add the function to the namespace and then to our contract

+ 128 - 0
tests/contract_testcases/solana/override.dot

@@ -0,0 +1,128 @@
+strict digraph "tests/contract_testcases/solana/override.sol" {
+	S [label="name:S\ntests/contract_testcases/solana/simple.sol:3:8-9\nfield name:f1 ty:int64\nfield name:f2 ty:bool"]
+	contract [label="contract C\ntests/contract_testcases/solana/override.sol:4:1-5:20"]
+	base [label="base A\ntests/contract_testcases/solana/override.sol:5:15-20"]
+	var [label="variable meh\nvisibility public\ntests/contract_testcases/solana/override.sol:6:2-32"]
+	meh [label="function meh\ncontract: C\ntests/contract_testcases/solana/override.sol:6:29-32\nsignature meh()\nvisibility public\nmutability view\noverride A"]
+	returns [label="returns\nint256 "]
+	return [label="return\nimplicit"]
+	storage_load [label="storage load int256\nimplicit"]
+	storage_var [label="storage variable\nC.meh\nint256 storage\nimplicit"]
+	contract_11 [label="contract A\ntests/contract_testcases/solana/override.sol:8:1-9:13"]
+	foo [label="function foo\ncontract: A\ntests/contract_testcases/solana/override.sol:10:5-43\nsignature foo()\nvisibility external\nmutability nonpayable\nvirtual"]
+	returns_13 [label="returns\nuint256 "]
+	contract_14 [label="contract B\ntests/contract_testcases/solana/override.sol:11:2-12:13"]
+	foo_15 [label="function foo\ncontract: B\ntests/contract_testcases/solana/override.sol:13:5-43\nsignature foo()\nvisibility external\nmutability nonpayable\nvirtual"]
+	returns_16 [label="returns\nuint256 "]
+	contract_17 [label="contract X\ntests/contract_testcases/solana/override.sol:14:2-15:19"]
+	base_18 [label="base A\ntests/contract_testcases/solana/override.sol:15:15-16"]
+	base_19 [label="base B\ntests/contract_testcases/solana/override.sol:15:18-19"]
+	var_20 [label="variable foo\nvisibility public\ntests/contract_testcases/solana/override.sol:16:9-39"]
+	foo_21 [label="function foo\ncontract: X\ntests/contract_testcases/solana/override.sol:16:36-39\nsignature foo()\nvisibility public\nmutability view\noverride A\noverride B"]
+	returns_22 [label="returns\nuint256 "]
+	return_23 [label="return\nimplicit"]
+	storage_load_24 [label="storage load uint256\nimplicit"]
+	storage_var_25 [label="storage variable\nX.foo\nuint256 storage\nimplicit"]
+	contract_26 [label="contract Y\ntests/contract_testcases/solana/override.sol:17:2-18:16"]
+	base_27 [label="base X\ntests/contract_testcases/solana/override.sol:18:15-16"]
+	contract_28 [label="contract A2\ntests/contract_testcases/solana/override.sol:20:1-21:22"]
+	foo_29 [label="function foo\ncontract: A2\ntests/contract_testcases/solana/override.sol:22:5-51\nsignature foo()\nvisibility external\nmutability nonpayable\nvirtual"]
+	returns_30 [label="returns\nuint256 "]
+	return_31 [label="return\ntests/contract_testcases/solana/override.sol:22:54-62"]
+	number_literal [label="uint256 literal: 1\ntests/contract_testcases/solana/override.sol:22:61-62"]
+	contract_33 [label="contract B2\ntests/contract_testcases/solana/override.sol:23:2-24:22"]
+	foo_34 [label="function foo\ncontract: B2\ntests/contract_testcases/solana/override.sol:25:5-51\nsignature foo()\nvisibility external\nmutability nonpayable\nvirtual"]
+	returns_35 [label="returns\nuint256 "]
+	return_36 [label="return\ntests/contract_testcases/solana/override.sol:25:54-62"]
+	number_literal_37 [label="uint256 literal: 2\ntests/contract_testcases/solana/override.sol:25:61-62"]
+	contract_38 [label="contract X2\ntests/contract_testcases/solana/override.sol:26:2-27:22"]
+	base_39 [label="base A2\ntests/contract_testcases/solana/override.sol:27:16-18"]
+	base_40 [label="base B2\ntests/contract_testcases/solana/override.sol:27:20-22"]
+	var_41 [label="variable foo\nvisibility public\ntests/contract_testcases/solana/override.sol:28:9-37"]
+	foo_42 [label="function foo\ncontract: X2\ntests/contract_testcases/solana/override.sol:28:34-37\nsignature foo()\nvisibility public\nmutability view\noverride A2"]
+	returns_43 [label="returns\nuint256 "]
+	return_44 [label="return\nimplicit"]
+	storage_load_45 [label="storage load uint256\nimplicit"]
+	storage_var_46 [label="storage variable\nX2.foo\nuint256 storage\nimplicit"]
+	contract_47 [label="contract Y2\ntests/contract_testcases/solana/override.sol:29:2-30:18"]
+	base_48 [label="base X2\ntests/contract_testcases/solana/override.sol:30:16-18"]
+	contract_49 [label="contract A\ntests/contract_testcases/solana/simple.sol:1:1-12"]
+	contract_50 [label="contract L\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	diagnostic [label="global variable has no bases contracts to override\nlevel Error\ntests/contract_testcases/solana/override.sol:3:14-22"]
+	diagnostic_53 [label="found contract 'C'\nlevel Debug\ntests/contract_testcases/solana/override.sol:4:1-5:20"]
+	diagnostic_54 [label="'meh' does not override anything\nlevel Error\ntests/contract_testcases/solana/override.sol:6:29-32"]
+	diagnostic_55 [label="found interface 'A'\nlevel Debug\ntests/contract_testcases/solana/override.sol:8:1-9:13"]
+	diagnostic_56 [label="found interface 'B'\nlevel Debug\ntests/contract_testcases/solana/override.sol:11:2-12:13"]
+	diagnostic_57 [label="found contract 'X'\nlevel Debug\ntests/contract_testcases/solana/override.sol:14:2-15:19"]
+	diagnostic_58 [label="found contract 'Y'\nlevel Debug\ntests/contract_testcases/solana/override.sol:17:2-18:16"]
+	diagnostic_59 [label="found abstract contract 'A2'\nlevel Debug\ntests/contract_testcases/solana/override.sol:20:1-21:22"]
+	diagnostic_60 [label="found abstract contract 'B2'\nlevel Debug\ntests/contract_testcases/solana/override.sol:23:2-24:22"]
+	diagnostic_61 [label="found contract 'X2'\nlevel Debug\ntests/contract_testcases/solana/override.sol:26:2-27:22"]
+	diagnostic_62 [label="function 'foo' missing overrides 'B2', specify 'override(B2,A2)'\nlevel Error\ntests/contract_testcases/solana/override.sol:28:21-33"]
+	diagnostic_63 [label="found contract 'Y2'\nlevel Debug\ntests/contract_testcases/solana/override.sol:29:2-30:18"]
+	diagnostic_64 [label="found contract 'A'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:1-12"]
+	diagnostic_65 [label="found library 'L'\nlevel Debug\ntests/contract_testcases/solana/simple.sol:1:14-2:11"]
+	structs -> S
+	contracts -> contract
+	contract -> base [label="base"]
+	contract -> var [label="variable"]
+	contract -> meh [label="function"]
+	meh -> returns [label="returns"]
+	meh -> return [label="body"]
+	return -> storage_load [label="expr"]
+	storage_load -> storage_var [label="expr"]
+	contracts -> contract_11
+	contract_11 -> foo [label="function"]
+	foo -> returns_13 [label="returns"]
+	contracts -> contract_14
+	contract_14 -> foo_15 [label="function"]
+	foo_15 -> returns_16 [label="returns"]
+	contracts -> contract_17
+	contract_17 -> base_18 [label="base"]
+	contract_17 -> base_19 [label="base"]
+	contract_17 -> var_20 [label="variable"]
+	contract_17 -> foo_21 [label="function"]
+	foo_21 -> returns_22 [label="returns"]
+	foo_21 -> return_23 [label="body"]
+	return_23 -> storage_load_24 [label="expr"]
+	storage_load_24 -> storage_var_25 [label="expr"]
+	contracts -> contract_26
+	contract_26 -> base_27 [label="base"]
+	contracts -> contract_28
+	contract_28 -> foo_29 [label="function"]
+	foo_29 -> returns_30 [label="returns"]
+	foo_29 -> return_31 [label="body"]
+	return_31 -> number_literal [label="expr"]
+	contracts -> contract_33
+	contract_33 -> foo_34 [label="function"]
+	foo_34 -> returns_35 [label="returns"]
+	foo_34 -> return_36 [label="body"]
+	return_36 -> number_literal_37 [label="expr"]
+	contracts -> contract_38
+	contract_38 -> base_39 [label="base"]
+	contract_38 -> base_40 [label="base"]
+	contract_38 -> var_41 [label="variable"]
+	contract_38 -> foo_42 [label="function"]
+	foo_42 -> returns_43 [label="returns"]
+	foo_42 -> return_44 [label="body"]
+	return_44 -> storage_load_45 [label="expr"]
+	storage_load_45 -> storage_var_46 [label="expr"]
+	contracts -> contract_47
+	contract_47 -> base_48 [label="base"]
+	contracts -> contract_49
+	contracts -> contract_50
+	diagnostics -> diagnostic [label="Error"]
+	diagnostics -> diagnostic_53 [label="Debug"]
+	diagnostics -> diagnostic_54 [label="Error"]
+	diagnostics -> diagnostic_55 [label="Debug"]
+	diagnostics -> diagnostic_56 [label="Debug"]
+	diagnostics -> diagnostic_57 [label="Debug"]
+	diagnostics -> diagnostic_58 [label="Debug"]
+	diagnostics -> diagnostic_59 [label="Debug"]
+	diagnostics -> diagnostic_60 [label="Debug"]
+	diagnostics -> diagnostic_61 [label="Debug"]
+	diagnostics -> diagnostic_62 [label="Error"]
+	diagnostics -> diagnostic_63 [label="Debug"]
+	diagnostics -> diagnostic_64 [label="Debug"]
+	diagnostics -> diagnostic_65 [label="Debug"]
+}

+ 32 - 0
tests/contract_testcases/solana/override.sol

@@ -0,0 +1,32 @@
+import "simple.sol" as IMP;
+
+int constant override no = 1;
+
+contract C is IMP.A {
+	int public override(IMP.A) meh;
+}
+
+interface A {
+    function foo() external returns (uint);
+}
+interface B {
+    function foo() external returns (uint);
+}
+contract X is A, B {
+        uint public override(A, B) foo;
+}
+contract Y is X {
+}
+
+abstract contract A2 {
+    function foo() virtual external returns (uint) { return 1; }
+}
+abstract contract B2 {
+    function foo() virtual external returns (uint) { return 2; }
+}
+contract X2 is A2, B2 {
+        uint public override(A2) foo;
+}
+contract Y2 is X2 {
+}
+

+ 2 - 2
tests/contract_testcases/substrate/inheritance/test_override_01.dot

@@ -8,8 +8,8 @@ strict digraph "tests/contract_testcases/substrate/inheritance/test_override_01.
 	variable [label="variable: a\nuint64\ntests/contract_testcases/substrate/inheritance/test_override_01.sol:4:24-25"]
 	number_literal [label="uint64 literal: 102\ntests/contract_testcases/substrate/inheritance/test_override_01.sol:4:28-31"]
 	diagnostic [label="found contract 'base'\nlevel Debug\ntests/contract_testcases/substrate/inheritance/test_override_01.sol:2:9-23"]
-	diagnostic_11 [label="function 'foo' does not override anything\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_01.sol:3:13-74"]
-	diagnostic_12 [label="contract 'bar' in override list not found\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_01.sol:3:45-48"]
+	diagnostic_11 [label="'foo' does not override anything\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_01.sol:3:13-74"]
+	diagnostic_12 [label="'bar' not found\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_01.sol:3:45-48"]
 	contracts -> contract
 	contract -> foo [label="function"]
 	foo -> parameters [label="parameters"]

+ 1 - 1
tests/contract_testcases/substrate/inheritance/test_override_02.dot

@@ -10,7 +10,7 @@ strict digraph "tests/contract_testcases/substrate/inheritance/test_override_02.
 	contract_9 [label="contract bar\ntests/contract_testcases/substrate/inheritance/test_override_02.sol:8:9-22"]
 	f [label="function f\ncontract: bar\ntests/contract_testcases/substrate/inheritance/test_override_02.sol:9:13-33\nsignature f()\nvisibility private\nmutability nonpayable"]
 	diagnostic [label="found contract 'base'\nlevel Debug\ntests/contract_testcases/substrate/inheritance/test_override_02.sol:2:9-23"]
-	diagnostic_13 [label="function 'foo' does not override anything\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_02.sol:3:13-74"]
+	diagnostic_13 [label="'foo' does not override anything\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_02.sol:3:13-74"]
 	diagnostic_14 [label="override 'bar' is not a base contract of 'base'\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_02.sol:3:45-48"]
 	diagnostic_15 [label="found contract 'bar'\nlevel Debug\ntests/contract_testcases/substrate/inheritance/test_override_02.sol:8:9-22"]
 	contracts -> contract

+ 1 - 1
tests/contract_testcases/substrate/inheritance/test_override_03.dot

@@ -8,7 +8,7 @@ strict digraph "tests/contract_testcases/substrate/inheritance/test_override_03.
 	variable [label="variable: a\nuint64\ntests/contract_testcases/substrate/inheritance/test_override_03.sol:4:24-25"]
 	number_literal [label="uint64 literal: 102\ntests/contract_testcases/substrate/inheritance/test_override_03.sol:4:28-31"]
 	diagnostic [label="found contract 'base'\nlevel Debug\ntests/contract_testcases/substrate/inheritance/test_override_03.sol:2:9-23"]
-	diagnostic_11 [label="function 'foo' does not override anything\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_03.sol:3:13-69"]
+	diagnostic_11 [label="'foo' does not override anything\nlevel Error\ntests/contract_testcases/substrate/inheritance/test_override_03.sol:3:13-69"]
 	contracts -> contract
 	contract -> foo [label="function"]
 	foo -> parameters [label="parameters"]