/* * Copyright 2010 Jon S Akhtar (Sylvanaar) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or aLuaeed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sylvanaar.idea.Lua.editor.inspections.utils; import com.intellij.psi.util.PsiTreeUtil; import com.sylvanaar.idea.Lua.lang.psi.LuaPsiElement; import com.sylvanaar.idea.Lua.lang.psi.expressions.LuaConditionalExpression; import com.sylvanaar.idea.Lua.lang.psi.statements.*; import com.sylvanaar.idea.Lua.lang.psi.visitor.LuaElementVisitor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @SuppressWarnings({"OverlyComplexClass"}) public class ControlFlowUtils { private ControlFlowUtils() { super(); } public static boolean statementMayCompleteNormally( @Nullable LuaStatementElement statement) { if (statement == null) { return true; } if (statement instanceof LuaBreakStatement || statement instanceof LuaReturnStatement) { return false; } if (statement instanceof LuaGenericForStatement || statement instanceof LuaNumericForStatement) { return forStatementMayReturnNormally(statement); } if (statement instanceof LuaWhileStatement) { return whileStatementMayReturnNormally( (LuaWhileStatement) statement); } if (statement instanceof LuaDoStatement) { return blockMayCompleteNormally(((LuaDoStatement) statement).getBlock()); } if (statement instanceof LuaBlock) { return blockMayCompleteNormally((LuaBlock) statement); } if (statement instanceof LuaIfThenStatement) { return ifStatementMayReturnNormally((LuaIfThenStatement) statement); } return true; } private static boolean whileStatementMayReturnNormally( @NotNull LuaWhileStatement loopStatement) { final LuaConditionalExpression test = loopStatement.getCondition(); return !BoolUtils.isTrue(test) || statementIsBreakTarget(loopStatement); } private static boolean forStatementMayReturnNormally( @NotNull LuaStatementElement loopStatement) { return true; } private static boolean ifStatementMayReturnNormally( @NotNull LuaIfThenStatement ifStatement) { final LuaBlock thenBranch = ifStatement.getIfBlock(); if (blockMayCompleteNormally(thenBranch)) { return true; } final LuaBlock elseBranch = ifStatement.getElseBlock(); return elseBranch == null || blockMayCompleteNormally(elseBranch); } public static boolean blockMayCompleteNormally( @Nullable LuaBlock block) { if (block == null) { return true; } final LuaStatementElement[] statements = block.getStatements(); for (final LuaStatementElement statement : statements) { if (!statementMayCompleteNormally(statement)) { return false; } } return true; } private static boolean statementIsBreakTarget( @NotNull LuaStatementElement statement) { final BreakFinder breakFinder = new BreakFinder(statement); statement.accept(breakFinder); return breakFinder.breakFound(); } public static boolean statementContainsReturn( @NotNull LuaStatementElement statement) { final ReturnFinder returnFinder = new ReturnFinder(); statement.accept(returnFinder); return returnFinder.returnFound(); } public static boolean isInLoop(@NotNull LuaPsiElement element) { final LuaConditionalLoop loop = PsiTreeUtil.getParentOfType(element, LuaConditionalLoop.class); if (loop == null) { return false; } final LuaBlock body = loop.getBody(); return PsiTreeUtil.isAncestor(body, element, true); } // private static boolean isInWhileStatementBody(@NotNull LuaPsiElement element) { // final LuaWhileStatement whileStatement = // PsiTreeUtil.getParentOfType(element, LuaWhileStatement.class); // if (whileStatement == null) { // return false; // } // final LuaStatementElement body = whileStatement.getBody(); // return PsiTreeUtil.isAncestor(body, element, true); // } // private static boolean isInForStatementBody(@NotNull LuaPsiElement element) { // final LuaForStatement forStatement = // PsiTreeUtil.getParentOfType(element, LuaForStatement.class); // if (forStatement == null) { // return false; // } // final LuaStatementElement body = forStatement.getBody(); // return PsiTreeUtil.isAncestor(body, element, true); // } // public static LuaStatementElement stripBraces(@NotNull LuaStatementElement branch) { // if (branch instanceof LuaBlockStatement) { // final LuaBlockStatement block = (LuaBlockStatement) branch; // final LuaStatement[] statements = block.getBlock().getStatements(); // if (statements.length == 1) { // return statements[0]; // } else { // return block; // } // } else { // return branch; // } // } public static boolean statementCompletesWithStatement( @NotNull LuaStatementElement containingStatement, @NotNull LuaStatementElement statement) { LuaPsiElement statementToCheck = statement; while (true) { if (statementToCheck.equals(containingStatement)) { return true; } final LuaPsiElement container = getContainingStatement(statementToCheck); if (container == null) { return false; } if (container instanceof LuaBlock) { if (!statementIsLastInBlock((LuaBlock) container, (LuaStatementElement) statementToCheck)) { return false; } } if (isLoop(container)) { return false; } statementToCheck = container; } } public static boolean blockCompletesWithStatement( @NotNull LuaBlock body, @NotNull LuaStatementElement statement) { LuaStatementElement statementToCheck = statement; while (true) { if (statementToCheck == null) { return false; } final LuaStatementElement container = getContainingStatement(statementToCheck); if (container == null) { return false; } if (isLoop(container)) { return false; } if (container instanceof LuaBlock) { if (!statementIsLastInBlock((LuaBlock) container, statementToCheck)) { return false; } if (container.equals(body)) { return true; } statementToCheck = PsiTreeUtil.getParentOfType(container, LuaStatementElement.class); } else { statementToCheck = container; } } } private static boolean isLoop(@NotNull LuaPsiElement element) { return element instanceof LuaConditionalLoop; } @Nullable private static LuaStatementElement getContainingStatement( @NotNull LuaPsiElement statement) { return PsiTreeUtil.getParentOfType(statement, LuaStatementElement.class); } @Nullable private static LuaPsiElement getContainingStatementOrBlock( @NotNull LuaPsiElement statement) { return PsiTreeUtil.getParentOfType(statement, LuaStatementElement.class, LuaBlock.class); } private static boolean statementIsLastInBlock(@NotNull LuaBlock block, @NotNull LuaStatementElement statement) { final LuaStatementElement[] statements = block.getStatements(); for (int i = statements.length - 1; i >= 0; i--) { final LuaStatementElement childStatement = statements[i]; if (statement.equals(childStatement)) { return true; } if (!(childStatement instanceof LuaReturnStatement)) { return false; } } return false; } private static boolean statementIsLastInCodeBlock(@NotNull LuaBlock block, @NotNull LuaStatementElement statement) { final LuaStatementElement[] statements = block.getStatements(); for (int i = statements.length - 1; i >= 0; i--) { final LuaStatementElement childStatement = statements[i]; if (statement.equals(childStatement)) { return true; } if (!(childStatement instanceof LuaReturnStatement)) { return false; } } return false; } private static class ReturnFinder extends LuaElementVisitor { private boolean m_found = false; public boolean returnFound() { return m_found; } public void visitReturnStatement( @NotNull LuaReturnStatement returnStatement) { if (m_found) { return; } super.visitReturnStatement(returnStatement); m_found = true; } } private static class BreakFinder extends LuaElementVisitor { private boolean m_found = false; private final LuaStatementElement m_target; private BreakFinder(@NotNull LuaStatementElement target) { super(); m_target = target; } public boolean breakFound() { return m_found; } public void visitBreakStatement( @NotNull LuaBreakStatement breakStatement) { if (m_found) { return; } super.visitBreakStatement(breakStatement); // TODO final LuaStatementElement exitedStatement = null; // TODO breakStatement.findTargetStatement(); if (exitedStatement == null) { return; } if (PsiTreeUtil.isAncestor(exitedStatement, m_target, false)) { m_found = true; } } } // public static boolean isInExitStatement(@NotNull LuaExpression expression) { // return isInReturnStatementArgument(expression) || // isInThrowStatementArgument(expression); // } // private static boolean isInReturnStatementArgument( // @NotNull LuaExpression expression) { // final LuaReturnStatement returnStatement = // PsiTreeUtil // .getParentOfType(expression, LuaReturnStatement.class); // return returnStatement != null; // } // private static boolean isInThrowStatementArgument( // @NotNull LuaExpression expression) { // final LuaThrowStatement throwStatement = // PsiTreeUtil // .getParentOfType(expression, LuaThrowStatement.class); // return throwStatement != null; // } // public interface ExitPointVisitor { // boolean visit(Instruction instruction); // } // // public static void visitAllExitPoints(@Nullable LuaBlock block, ExitPointVisitor visitor) { // if (block == null) return; // final Instruction[] flow = block.getControlFlow(); // boolean[] visited = new boolean[flow.length]; // visitAllExitPointsInner(flow[flow.length - 1], flow[0], visited, visitor); // } // // private static boolean visitAllExitPointsInner(Instruction last, Instruction first, boolean[] visited, ExitPointVisitor visitor) { // if (first == last) return true; // if (last instanceof MaybeReturnInstruction) { // return visitor.visit(last); // } // // final PsiElement element = last.getElement(); // if (element != null) { // return visitor.visit(last); // } // visited[last.num()] = true; // for (Instruction pred : last.allPred()) { // if (!visited[pred.num()]) { // if (!visitAllExitPointsInner(pred, first, visited, visitor)) return false; // } // } // return true; // } }