MODULE M3CChkReturn; (***************************************************************************) (* Copyright (C) Olivetti 1989 *) (* All Rights reserved *) (* *) (* Use and copy of this software and preparation of derivative works based *) (* upon this software are permitted to any person, provided this same *) (* copyright notice and the following Olivetti warranty disclaimer are *) (* included in any copy of the software or any modification thereof or *) (* derivative work therefrom made by any person. *) (* *) (* This software is made available AS IS and Olivetti disclaims all *) (* warranties with respect to this software, whether expressed or implied *) (* under any law, including all implied warranties of merchantibility and *) (* fitness for any purpose. In no event shall Olivetti be liable for any *) (* damages whatsoever resulting from loss of use, data or profits or *) (* otherwise arising out of or in connection with the use or performance *) (* of this software. *) (***************************************************************************) IMPORT Text; IMPORT AST, M3AST_AS; IMPORT M3AST_AS_F, M3AST_TM_F; IMPORT SeqM3AST_AS_STM; IMPORT ASTWalk; IMPORT M3CPragma, M3Error; TYPE Stack = BRANDED REF RECORD next: Stack; procDecl: M3AST_AS.Proc_decl; stm: M3AST_AS.STM; ignoring := TRUE; inLoop := 0; returnInLoop := FALSE; END; (* record *) REVEAL Handle = ASTWalk.Closure BRANDED OBJECT cu: M3AST_AS.Compilation_Unit; stack: Stack := NIL; OVERRIDES callback := Node; END; (* record *) <*INLINE*> PROCEDURE DoWarning(n: M3Error.ERROR_NODE; m: TEXT) RAISES {}= BEGIN M3Error.Warn(n, m); END DoWarning; PROCEDURE NeedsReturnCheck(s: Stack) RAISES {}= BEGIN WHILE s # NIL DO IF s.procDecl # NIL THEN s.procDecl.tmp_needs_return_check := TRUE; RETURN; END; s := s.next; END; END NeedsReturnCheck; PROCEDURE NotReachedAfterSrcNode( h: Handle; srcNode: M3AST_AS.SRC_NODE) : BOOLEAN RAISES {}= VAR iter := M3CPragma.AfterNode(h.cu.lx_pragmas, srcNode); pragma: M3CPragma.T; args: Text.T; BEGIN WHILE M3CPragma.Next(iter, pragma) AND M3CPragma.PrecedingNode(pragma) = srcNode DO IF M3CPragma.Match(pragma, "NOTREACHED", args) AND args = NIL THEN RETURN TRUE; END; END; RETURN FALSE; END NotReachedAfterSrcNode; PROCEDURE PushLastStm( h: Handle; srcNode: M3AST_AS.SRC_NODE; seqSTM: SeqM3AST_AS_STM.T; procDecl: M3AST_AS.Proc_decl := NIL) RAISES {}= VAR last, stm: M3AST_AS.STM := NIL; iter := SeqM3AST_AS_STM.NewIter(seqSTM); BEGIN WHILE SeqM3AST_AS_STM.Next(iter, stm) DO last := stm END; IF last = NIL THEN IF NOT NotReachedAfterSrcNode(h, srcNode) THEN DoWarning(srcNode, "last statement in function is not a RETURN"); END; IF procDecl # NIL THEN procDecl.tmp_needs_return_check := TRUE; ELSE NeedsReturnCheck(h.stack); END; ELSE h.stack := NEW(Stack, next := h.stack, procDecl := procDecl, stm := last); END; END PushLastStm; PROCEDURE NotReachedAfterStm(h: Handle; stm: M3AST_AS.STM): BOOLEAN RAISES {}= VAR iter := M3CPragma.AfterStmOrDecl(h.cu.lx_pragmas, stm); pragma: M3CPragma.T; args: Text.T; BEGIN WHILE M3CPragma.Next(iter, pragma) AND M3CPragma.PrecedingStmOrDecl(pragma) = stm DO IF M3CPragma.Match(pragma, "NOTREACHED", args) AND args = NIL THEN RETURN TRUE; END; END; RETURN FALSE; END NotReachedAfterStm; PROCEDURE Node( h: Handle; n: AST.NODE; vm: ASTWalk.VisitMode) RAISES {}= BEGIN IF vm = ASTWalk.VisitMode.Entry THEN TYPECASE n OF | M3AST_AS.Proc_decl(procDecl) => VAR body := procDecl.as_body; BEGIN IF body # NIL AND procDecl.as_type.as_result_type # NIL THEN PushLastStm(h, body, body.as_stm_s, procDecl); END; END; ELSE IF h.stack # NIL THEN IF h.stack.ignoring THEN IF h.stack.stm # n THEN RETURN END; TYPECASE n OF | M3AST_AS.Assign_st, M3AST_AS.Call_st, M3AST_AS.Exit_st, M3AST_AS.Eval_st => NeedsReturnCheck(h.stack); IF NOT NotReachedAfterStm(h, h.stack.stm) THEN DoWarning(h.stack.stm, "last statement in function is not a RETURN"); END; | M3AST_AS.Return_st, M3AST_AS.Raise_st => ELSE (* Complex statement, containing sub statements *) h.stack.ignoring := FALSE; TYPECASE n OF | M3AST_AS.While_st, M3AST_AS.Repeat_st, M3AST_AS.Loop_st, M3AST_AS.For_st => INC(h.stack.inLoop); NeedsReturnCheck(h.stack); | M3AST_AS.If_st(ifSt) => IF ifSt.as_else = NIL THEN NeedsReturnCheck(h.stack); IF NOT NotReachedAfterStm(h, ifSt) THEN DoWarning(ifSt, "last statement in function is IF with no ELSE"); END; ELSE PushLastStm(h, ifSt, ifSt.as_stm_s); END; | M3AST_AS.STM_WSS(stmWSS) => PushLastStm(h, stmWSS, stmWSS.as_stm_s); ELSE END; END; RETURN; ELSE IF h.stack.inLoop > 0 THEN TYPECASE n OF | M3AST_AS.Return_st => h.stack.returnInLoop := TRUE; | M3AST_AS.Exit_st(exitSt) => IF h.stack.inLoop = 1 THEN DoWarning(exitSt, "EXIT will leave function without returning value"); END; | M3AST_AS.While_st, M3AST_AS.Repeat_st, M3AST_AS.Loop_st, M3AST_AS.For_st => INC(h.stack.inLoop); ELSE END; ELSE TYPECASE n OF | M3AST_AS.Try_except => (* AST problem; not really a SUBSTM_WSS at all.. *) | M3AST_AS.SUBSTM_WSS(subStmWSS) => PushLastStm(h, subStmWSS, subStmWSS.as_stm_s); ELSE END; END; END; END; END; (* typecase *) ELSE IF h.stack # NIL THEN IF h.stack.stm = n THEN IF h.stack.inLoop # 0 AND NOT h.stack.returnInLoop THEN IF NOT NotReachedAfterStm(h, h.stack.stm) THEN DoWarning(h.stack.stm, "loop at end of function does not contain RETURN"); END; END; h.stack := h.stack.next; ELSIF h.stack.inLoop > 1 THEN TYPECASE n OF | M3AST_AS.While_st, M3AST_AS.Repeat_st, M3AST_AS.Loop_st, M3AST_AS.For_st => DEC(h.stack.inLoop); ELSE END; END; END; END; END Node; PROCEDURE NewHandle(cu: M3AST_AS.Compilation_Unit): Handle RAISES {}= BEGIN RETURN NEW(Handle, cu := cu); END NewHandle; BEGIN END M3CChkReturn.