private static boolean containsImplicitReturnOfUndefined(FunctionTree tree) { BlockTree body = (BlockTree) tree.body(); FunctionReturns functionReturns = FunctionReturns.getFunctionReturns(body); return functionReturns.containsImplicitReturn() || functionReturns.containsReturnWithoutValue(); }
@Override public void visitNode(Tree tree) { FunctionTree functionTree = (FunctionTree) tree; if (functionTree.body().is(Kind.BLOCK)) { BlockTree body = (BlockTree) functionTree.body(); FunctionReturns functionReturns = getFunctionReturns(body); if (functionReturns.containsReturnWithoutValue() && functionReturns.containsReturnWithValue()) { raiseIssue(tree, functionReturns, body); } } }
public static FunctionReturns getFunctionReturns(BlockTree functionBody) { FunctionReturns functionReturns = new FunctionReturns(); ControlFlowGraph cfg = ControlFlowGraph.build(functionBody); if (containsTry(cfg)) { return functionReturns; } CfgBlock endBlock = cfg.end(); // Possible predecessors for end block: // * return statement -> check for expression // * last statement in function -> implicit return without value // * throw statement -> we only target return here, so ignore for (CfgBlock cfgBlock : endBlock.predecessors()) { Tree lastElement = cfgBlock.elements().get(cfgBlock.elements().size() - 1); if (lastElement.is(Kind.RETURN_STATEMENT)) { ReturnStatementTree returnStatement = (ReturnStatementTree) lastElement; if (returnStatement.expression() == null) { functionReturns.containsReturnWithoutValue = true; } else { functionReturns.containsReturnWithValue = true; } functionReturns.returnStatements.add(returnStatement); } else if (!isThrowStatement(lastElement) && isReachableBlock(cfgBlock, cfg)) { functionReturns.containsReturnWithoutValue = true; functionReturns.containsImplicitReturn = true; } } return functionReturns; }
private void raiseIssue(Tree functionTree, FunctionReturns functionReturns, BlockTree body) { SyntaxToken tokenToRaiseIssue = functionTree.firstToken(); if (functionTree.is(Kind.ARROW_FUNCTION)) { tokenToRaiseIssue = ((ArrowFunctionTree) functionTree).doubleArrowToken(); } if (functionTree.is(Kind.GENERATOR_METHOD)) { tokenToRaiseIssue = ((MethodDeclarationTree) functionTree).name().firstToken(); } PreciseIssue issue = addIssue(tokenToRaiseIssue, MESSAGE); for (ReturnStatementTree returnStatement : functionReturns.returnStatements()) { issue.secondary(returnStatement.returnKeyword(), returnStatement.expression() == null ? "Return without value" : "Return with value"); } if (functionReturns.containsImplicitReturn()) { issue.secondary(body.closeCurlyBraceToken(), "Implicit return without value"); } }