@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();
 }