diff --git a/lslopt/lslparse.py b/lslopt/lslparse.py index b3df766..e30b5a8 100644 --- a/lslopt/lslparse.py +++ b/lslopt/lslparse.py @@ -1791,7 +1791,8 @@ list lazy_list_set(list L, integer i, list v) if AllowStSw is False: raise EParseCantChangeState(self) if AllowStSw is None: - self.SuspiciousStSw.append(self.errorpos) + self.PruneBug.append((self.errorpos, + EParseCantChangeState)) self.NextToken() if self.tok[0] not in ('DEFAULT', 'IDENT'): raise EParseSyntax(self) @@ -1812,12 +1813,31 @@ list lazy_list_set(list L, integer i, list v) value = None else: savepos = self.errorpos + saveAllowVoid = self.allowVoid + # Needed due to another LSL bug, see regr/void-in-return.lsl + self.allowVoid = True value = self.Parse_expression() + self.allowVoid = saveAllowVoid self.expect(';') self.NextToken() if ReturnType is None and value is not None: - self.errorpos = savepos - raise EParseReturnShouldBeEmpty(self) + # It follows the same rules as AllowStSw + if AllowStSw is False: + self.errorpos = savepos + raise EParseReturnShouldBeEmpty(self) + elif value.t is None: + if AllowStSw is None: + self.PruneBug.append((self.errorpos, + EParseReturnShouldBeEmpty)) + self.PushScope() + scope = self.scopeindex + self.PopScope() + return nr(nt='{}', t=None, scope=scope, + ch=[nr(nt='EXPR', t=None, ch=[value]), + nr(nt='RETURN', t=None)]) + else: + self.errorpos = savepos + raise EParseTypeMismatch(self) if ReturnType is not None and value is None: self.errorpos = savepos raise EParseReturnIsEmpty(self) @@ -1835,22 +1855,22 @@ list lazy_list_set(list L, integer i, list v) ret.ch.append(self.Parse_expression()) self.expect(')') self.NextToken() - saveSuspiciousStSw = self.SuspiciousStSw - self.SuspiciousStSw = [] + savePruneBug = self.PruneBug + self.PruneBug = [] ret.ch.append(self.Parse_statement(ReturnType, AllowStSw = None, InsideLoop = InsideLoop)) if self.tok[0] == 'ELSE': - if AllowStSw is False and self.SuspiciousStSw: - self.errorpos = self.SuspiciousStSw[0] - raise EParseCantChangeState(self) + if AllowStSw is False and self.PruneBug: + self.errorpos = self.PruneBug[0][0] + raise self.PruneBug[0][1](self) LastIsReturn = getattr(ret.ch[1], 'LIR', False) self.NextToken() ret.ch.append(self.Parse_statement(ReturnType, AllowStSw = AllowStSw, InsideLoop = InsideLoop)) if AllowStSw is None: - saveSuspiciousStSw += self.SuspiciousStSw + savePruneBug += self.PruneBug if LastIsReturn and getattr(ret.ch[2], 'LIR', False): ret.LIR = True - self.SuspiciousStSw = saveSuspiciousStSw + self.PruneBug = savePruneBug return ret if tok0 == 'WHILE': @@ -2915,8 +2935,10 @@ list lazy_list_set(list L, integer i, list v) # List of preprocessor #line directives. self.linedir = [] - # List of positions with suspicious state change statements. - self.SuspiciousStSw = [] + # List of tuples (position, exception) where suspicious state change + # statements or returns with void expressions happen. These can only + # be detected when the 'else' is found. + self.PruneBug = [] # This is a small hack to prevent circular definitions in globals when # extended expressions are enabled. When false (default), forward diff --git a/run-tests.py b/run-tests.py index 75c8cce..ffda0b8 100755 --- a/run-tests.py +++ b/run-tests.py @@ -368,8 +368,19 @@ class UnitTestRegression(UnitTestCase): parser.parse('default{timer(){[llDie()];}}') parser.parse('default{timer(){llDie();}}') parser.parse('default{timer(){(llDie());}}') - parser.parse('default{timer(){for(llDie();1;llDie());}}' - , ('optimize',)) + parser.parse('default{timer(){for(llDie();1;llDie());}}', + ('optimize',)) + # 'return ' works in the same situations as state changes + self.assertRaises(lslparse.EParseReturnShouldBeEmpty, parser.parse, + 'default{timer(){return llDie();}}') + self.assertRaises(lslparse.EParseReturnShouldBeEmpty, parser.parse, + 'default{timer(){if(1)return llDie();else;}}') + self.assertRaises(lslparse.EParseReturnShouldBeEmpty, parser.parse, + 'default{timer(){if(1);else return llDie();}}') + self.assertRaises(lslparse.EParseReturnShouldBeEmpty, parser.parse, + 'default{timer(){return 1;}}') + self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, + 'default{timer(){if(1)return 1;}}') class UnitTestCoverage(UnitTestCase): def test_coverage_misc(self): diff --git a/unit_tests/regression.suite/void-in-return.lsl b/unit_tests/regression.suite/void-in-return.lsl new file mode 100644 index 0000000..225b26a --- /dev/null +++ b/unit_tests/regression.suite/void-in-return.lsl @@ -0,0 +1,41 @@ +x(){ + // All these should work + if (1) + return llDie(); + while (1) + return llDie(); + do + return llDie(); + while (1); + for (;1;) + return llDie(); + if (1) + { + return llDie(); + if (1) return llDie(); else return llDie(); + return llDie(); + } +} + +default +{ + state_entry() + { + // Similarly, all these should work + if (1) + return llDie(); + while (1) + return llDie(); + do + return llDie(); + while (1); + for (;1;) + return llDie(); + if (1) + { + return llDie(); + if (1) return llDie(); else return llDie(); + return llDie(); + } + } +} diff --git a/unit_tests/regression.suite/void-in-return.out b/unit_tests/regression.suite/void-in-return.out new file mode 100644 index 0000000..7a615f8 --- /dev/null +++ b/unit_tests/regression.suite/void-in-return.out @@ -0,0 +1,94 @@ +x() +{ + if (1) + { + llDie(); + return; + } + while (1) + { + llDie(); + return; + } + do + { + llDie(); + return; + } + while (1); + for (; 1; ) + { + llDie(); + return; + } + if (1) + { + { + llDie(); + return; + } + if (1) + { + llDie(); + return; + } + else + { + llDie(); + return; + } + { + llDie(); + return; + } + } +} + +default +{ + state_entry() + { + if (1) + { + llDie(); + return; + } + while (1) + { + llDie(); + return; + } + do + { + llDie(); + return; + } + while (1); + for (; 1; ) + { + llDie(); + return; + } + if (1) + { + { + llDie(); + return; + } + if (1) + { + llDie(); + return; + } + else + { + llDie(); + return; + } + { + llDie(); + return; + } + } + } +} diff --git a/unit_tests/regression.suite/void-in-return.run b/unit_tests/regression.suite/void-in-return.run new file mode 100644 index 0000000..91edf6f --- /dev/null +++ b/unit_tests/regression.suite/void-in-return.run @@ -0,0 +1 @@ +main.py - -O -optimize