docs: def _analyze_function_features(self, all_funcs_completed=False):
          """
          For each function in the function_manager, try to determine if it returns or not. A function does not return if
          it calls another function that is known to be not returning, and this function does not have other exits.
  
          We might as well analyze other features of functions in the future.
  
          :param bool all_funcs_completed:    Ignore _completed_functions set and treat all functions as completed. This
                                              can be set to True after the entire CFG is built and _post_analysis() is
                                              called (at which point analysis on all functions must be completed).
          """
  
          changes = {
              'functions_return': [],
              'functions_do_not_return': []
          }
  
          if self._updated_nonreturning_functions is not None:
              all_func_addrs = self._updated_nonreturning_functions
  
              # Convert addresses to objects
              all_functions = [ self.kb.functions.get_by_addr(f) for f in all_func_addrs
                                if self.kb.functions.contains_addr(f) ]
  
          else:
              all_functions = list(self.kb.functions.values())
  
          analyzed_functions = set()
          # short-hand
          functions = self.kb.functions  # type: angr.knowledge.FunctionManager
  
          while all_functions:
              func = all_functions.pop(-1)  # type: angr.knowledge.Function
              analyzed_functions.add(func.addr)
  
              if func.returning is not None:
                  # It has been determined before. Skip it
                  continue
  
              returning = self._determine_function_returning(func, all_funcs_completed=all_funcs_completed)
  
              if returning:
                  func.returning = True
                  changes['functions_return'].append(func)
              elif returning is False:
                  func.returning = False
                  changes['functions_do_not_return'].append(func)
  
              if returning is not None:
                  # Add all callers of this function to all_functions list
                  if func.addr in functions.callgraph:
                      callers = functions.callgraph.predecessors(func.addr)
                      for caller in callers:
                          if caller in analyzed_functions:
                              continue
                          if functions.contains_addr(caller):
                              all_functions.append(functions.get_by_addr(caller))
  
          return changes