# V1090. The 'std::uncaught_exception' function is deprecated since C++17 and is removed in C++20. Consider replacing this function with 'std::uncaught_exceptions'.

Анализатор обнаружил вызов функции 'std::uncaught_exception'. Применение этой функции может привести к неверной логике программы. Начиная с C++17, она признана устаревшей и должна быть заменена на функцию 'std::uncaught_exceptions'.

Функция 'std::uncaught_exception' обычно применяется для того, чтобы понять, вызывается ли код при раскрутке стека. Рассмотрим пример:

    constexpr std::string_view defaultSymlinkPath = "system/logs/log.txt";

    class Logger
    {
      std::string   m_fileName;
      std::ofstream m_fileStream;

      Logger(const char *filename)
        : m_fileName { filename }
        , m_fileStream { m_fileName }
      {
      }

      void Log(std::string_view);

      ~Logger()
      {
        fileStream.close();
        if (!std::uncaught_exception())
        {
          std::filesystem::create_symlink(m_fileName, defaultSymlinkPath);
        }
      }
    };

    class Calculator
    {
    public:
      int64_t Calc(const std::vector<std::string> &params);
      // ....
      ~Calculator()
      {
        try
        {
          Logger logger("log.txt");
          Logger.Log("Calculator destroyed");
        }
        catch (...)
        {
          // ....
        }
      }
    }

    int64_t Process(const std::vector<std::string> &params)
    {
      try
      {
        Calculator calculator;
        return Calculator.Calc(params);
      }
      catch (...)
      {
        // ....
      }
    }

В деструкторе класса 'Logger' вызывается функция 'std::filesystem::create_symlink', которая может бросить исключение. Например, если для использования пути 'system/logs/log.txt' у программы недостаточно прав. Если деструктор 'Logger' будет вызван напрямую в результате раскрутки стека, то бросать исключения из этого деструктора нельзя – программа будет аварийно прервана через 'std::terminate'. Поэтому перед вызовом функции программист сделал дополнительную проверку 'if (!std::uncaught_exception())'.

Однако такой код содержит ошибку. Предположим, что функция 'Calc' бросила исключение. Тогда перед выполнением catch-clause произойдёт вызов деструктора 'Calculator'. В нём будет создан экземпляр класса 'Logger', в лог запишется сообщение. Затем будет вызван деструктор 'Logger'. Внутри него произойдёт вызов функции 'std::uncaught_exception'. Эта функция вернёт 'true', потому что исключение, брошенное функцией 'Calc', ещё не перехвачено. Поэтому символическая ссылка для файла с логом не будет создана.

Однако в данном случае можно попробовать создать символическую ссылку. Дело в том, что деструктор 'Logger' будет вызван не напрямую в результате раскрутки стека, а из деструктора 'Calculator'. Поэтому из деструктора 'Logger' можно бросить исключение — нужно только перехватить его до выхода из деструктора 'Calculator'.

Для исправления необходимо воспользоваться функцией 'std::uncaught_exceptions' из C++17:

    class Logger
    {
      std::string   m_fileName;
      std::ofstream m_fileStream;
      int           m_exceptions = std::uncaught_exceptions(); // <=

      Logger(const char *filename)
        : m_fileName { filename }
        , m_fileStream { m_fileName }
      {
      }

      ~Logger()
      {
        fileStream.close();
        if (m_exceptions == std::uncaught_exceptions())
        {
          std::filesystem::create_symlink(m_fileName, defaultSymlinkPath);
        }
      }
    };

Теперь при создании объекта класса 'Logger' в поле 'm_exceptions' сохранится текущее количество неперехваченных исключений. Если между созданием объекта и вызовом его деструктора не было брошено новых исключений, то условие будет истинным. Поэтому программа попробует создать символическую ссылку для файла с логом. Если при этом будет брошено исключение, то оно будет перехвачено и обработано в деструкторе 'Calculator', и программа продолжит выполнение.

Данная диагностика классифицируется как:

CWE-248