| /** |
| * Used to help transform statement AST into flow graph. |
| * |
| * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/stmtstate.d, _stmtstate.d) |
| * Documentation: https://dlang.org/phobos/dmd_stmtstate.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/stmtstate.d |
| */ |
| |
| module dmd.stmtstate; |
| |
| import dmd.identifier; |
| import dmd.statement; |
| |
| |
| /************************************************ |
| * Used to traverse the statement AST to transform it into |
| * a flow graph. |
| * Keeps track of things like "where does the `break` go". |
| * Params: |
| * block = type of the flow graph node |
| */ |
| struct StmtState(block) |
| { |
| StmtState* prev; |
| Statement statement; |
| |
| Identifier ident; |
| block* breakBlock; |
| block* contBlock; |
| block* switchBlock; |
| block* defaultBlock; |
| block* finallyBlock; |
| block* tryBlock; |
| |
| this(StmtState* prev, Statement statement) |
| { |
| this.prev = prev; |
| this.statement = statement; |
| if (prev) |
| this.tryBlock = prev.tryBlock; |
| } |
| |
| block* getBreakBlock(Identifier ident) |
| { |
| StmtState* bc; |
| if (ident) |
| { |
| Statement related = null; |
| block* ret = null; |
| for (bc = &this; bc; bc = bc.prev) |
| { |
| // The label for a breakBlock may actually be some levels up (e.g. |
| // on a try/finally wrapping a loop). We'll see if this breakBlock |
| // is the one to return once we reach that outer statement (which |
| // in many cases will be this same statement). |
| if (bc.breakBlock) |
| { |
| related = bc.statement.getRelatedLabeled(); |
| ret = bc.breakBlock; |
| } |
| if (bc.statement == related && bc.prev.ident == ident) |
| return ret; |
| } |
| } |
| else |
| { |
| for (bc = &this; bc; bc = bc.prev) |
| { |
| if (bc.breakBlock) |
| return bc.breakBlock; |
| } |
| } |
| return null; |
| } |
| |
| block* getContBlock(Identifier ident) |
| { |
| StmtState* bc; |
| if (ident) |
| { |
| block* ret = null; |
| for (bc = &this; bc; bc = bc.prev) |
| { |
| // The label for a contBlock may actually be some levels up (e.g. |
| // on a try/finally wrapping a loop). We'll see if this contBlock |
| // is the one to return once we reach that outer statement (which |
| // in many cases will be this same statement). |
| if (bc.contBlock) |
| { |
| ret = bc.contBlock; |
| } |
| if (bc.prev && bc.prev.ident == ident) |
| return ret; |
| } |
| } |
| else |
| { |
| for (bc = &this; bc; bc = bc.prev) |
| { |
| if (bc.contBlock) |
| return bc.contBlock; |
| } |
| } |
| return null; |
| } |
| |
| block* getSwitchBlock() |
| { |
| StmtState* bc; |
| for (bc = &this; bc; bc = bc.prev) |
| { |
| if (bc.switchBlock) |
| return bc.switchBlock; |
| } |
| return null; |
| } |
| |
| block* getDefaultBlock() |
| { |
| StmtState* bc; |
| for (bc = &this; bc; bc = bc.prev) |
| { |
| if (bc.defaultBlock) |
| return bc.defaultBlock; |
| } |
| return null; |
| } |
| |
| block* getFinallyBlock() |
| { |
| StmtState* bc; |
| for (bc = &this; bc; bc = bc.prev) |
| { |
| if (bc.finallyBlock) |
| return bc.finallyBlock; |
| } |
| return null; |
| } |
| } |