From 3bd3c31210c07c81dbd0d9c954b4b5989132b012 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= <tseeker@nocternity.net>
Date: Mon, 2 Jan 2023 20:29:32 +0100
Subject: [PATCH] Parser - Prevent return from being used outside of functions

---
 src/parser.rs | 36 ++++++++++++++++++++++++++++--------
 1 file changed, 28 insertions(+), 8 deletions(-)

diff --git a/src/parser.rs b/src/parser.rs
index 51d8fd2..91cf5c1 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -440,15 +440,22 @@ impl Parser {
     /// return_statement := "return" expression? ";"
     /// ```
     fn parse_return_statement(&mut self, ret_token: &Token) -> ParserResult<ast::StmtNode> {
-        let value = if self.check(&TokenType::Semicolon) {
-            None
+        if self.can_use_return() {
+            let value = if self.check(&TokenType::Semicolon) {
+                None
+            } else {
+                Some(self.parse_expression()?)
+            };
+            Ok(ast::StmtNode::Return {
+                token: ret_token.clone(),
+                value,
+            })
         } else {
-            Some(self.parse_expression()?)
-        };
-        Ok(ast::StmtNode::Return {
-            token: ret_token.clone(),
-            value,
-        })
+            Err(ParserError::new(
+                ret_token,
+                "'return' found outside of function",
+            ))
+        }
     }
 
     /// Parse the following rule:
@@ -758,4 +765,17 @@ impl Parser {
         }
         false
     }
+
+    /// Check whether the `return` keyword can be used. This is true whenever
+    /// the first `LoopParsingState::None` found in the loop parsing state is
+    /// not the one at position 0.
+    fn can_use_return(&self) -> bool {
+        let mut pos = self.loop_state.len() - 1;
+        loop {
+            if self.loop_state[pos] == LoopParsingState::None {
+                return pos == 0;
+            }
+            pos -= 1;
+        }
+    }
 }