Browse Source

Detect missing return statement after if-then-else

If then part is not reachable, the else part may be.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 năm trước cách đây
mục cha
commit
2d79df9117
3 tập tin đã thay đổi với 33 bổ sung5 xóa
  1. 9 4
      src/sema/printer.rs
  2. 1 1
      src/sema/statements.rs
  3. 23 0
      tests/substrate_tests/functions.rs

+ 9 - 4
src/sema/printer.rs

@@ -313,20 +313,25 @@ fn print_statement(stmts: &[Statement], func: &Function, ns: &Namespace) -> Vec<
                 format!("declare {} {}", p.ty.to_string(ns), p.name),
                 vec![print_expr(e, Some(func), ns)],
             ),
-            Statement::If(_, _, cond, then_stmt, else_stmt) if else_stmt.is_empty() => {
+            Statement::If(_, reachable, cond, then_stmt, else_stmt) if else_stmt.is_empty() => {
+                let reachable = Tree::Leaf(format!("reachable:{}", reachable));
                 let cond =
                     Tree::Branch(String::from("cond"), vec![print_expr(cond, Some(func), ns)]);
                 let then = Tree::Branch(String::from("then"), print_statement(then_stmt, func, ns));
-                Tree::Branch(String::from("if"), vec![cond, then])
+                Tree::Branch(String::from("if"), vec![cond, then, reachable])
             }
-            Statement::If(_, _, cond, then_stmt, else_stmt) => {
+            Statement::If(_, reachable, cond, then_stmt, else_stmt) => {
+                let reachable = Tree::Leaf(format!("reachable:{}", reachable));
                 let cond =
                     Tree::Branch(String::from("cond"), vec![print_expr(cond, Some(func), ns)]);
                 let then_tree =
                     Tree::Branch(String::from("then"), print_statement(then_stmt, func, ns));
                 let else_tree =
                     Tree::Branch(String::from("else"), print_statement(else_stmt, func, ns));
-                Tree::Branch(String::from("if"), vec![cond, then_tree, else_tree])
+                Tree::Branch(
+                    String::from("if"),
+                    vec![cond, then_tree, else_tree, reachable],
+                )
             }
             Statement::While(_, _, cond, body) => {
                 let cond =

+ 1 - 1
src/sema/statements.rs

@@ -449,7 +449,7 @@ fn statement(
             let mut else_stmts = Vec::new();
             if let Some(stmts) = else_ {
                 symtable.new_scope();
-                reachable &= statement(
+                reachable |= statement(
                     stmts,
                     &mut else_stmts,
                     file_no,

+ 23 - 0
tests/substrate_tests/functions.rs

@@ -596,6 +596,29 @@ fn args_and_returns() {
 
     assert_eq!(first_error(ns.diagnostics), "missing return statement");
 
+    let src = "
+    contract primitives {
+        enum oper { add, sub, mul, div, mod, pow }
+
+        function op_i64(oper op, int64 a, int64 b) pure public returns (int64) {
+            if (op == oper.add) {
+                return a + b;
+            } else if (op == oper.sub) {
+                return a - b;
+            } else if (op == oper.mul) {
+                return a * b;
+            } else if (op == oper.div) {
+                return a / b;
+            } else if (op == oper.mod) {
+                return a % b;
+            }
+        }
+    }";
+
+    let ns = parse_and_resolve(&src, Target::Substrate);
+
+    assert_eq!(first_error(ns.diagnostics), "missing return statement");
+
     let mut runtime = build_solidity(
         "
         contract foobar {