@Override public void visit(NodeTraversal t, Node n, Node parent) { // Do the checks when 'n' is the block node and 'parent' is the function // node, so that getControlFlowGraph will return the graph inside // the function, rather than the graph of the enclosing scope. if (hasReturnDeclaredNullable(n) && !canReturnNull(t.getControlFlowGraph())) { String fnName = NodeUtil.getNearestFunctionName(parent); if (fnName != null && !fnName.isEmpty()) { compiler.report(t.makeError(parent, NULLABLE_RETURN_WITH_NAME, fnName)); } else { compiler.report(t.makeError(parent, NULLABLE_RETURN)); } } }
/** * @return True if n is a function node which is explicitly annotated as returning a nullable * type, other than {?}. */ private static boolean isReturnTypeNullable(Node n) { if (n == null || !n.isFunction()) { return false; } FunctionType functionType = n.getJSType().toMaybeFunctionType(); if (functionType == null) { // If the JSDoc declares a non-function type on a function node, we still shouldn't crash. return false; } JSType returnType = functionType.getReturnType(); if (returnType == null || returnType.isUnknownType() || !returnType.isNullable()) { return false; } JSDocInfo info = NodeUtil.getBestJSDocInfo(n); return info != null && info.hasReturnType(); }