Repository: microsoft/terminal
File: src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp (C++)

Code before:
void CascadiaSettings::_validateProfileEnvironmentVariables()
{
    for (const auto& profile : _allProfiles)
    {
        std::set<std::wstring, til::env_key_sorter> envVarNames{};
        if (profile.EnvironmentVariables() == nullptr)
        {
            continue;
        }
        for (const auto [key, value] : profile.EnvironmentVariables())
        {
            const auto iterator = envVarNames.insert(key.c_str());
            if (!iterator.second)
            {
                _warnings.Append(SettingsLoadWarnings::InvalidProfileEnvironmentVariables);
                return;
            }
        }
    }
}

static void _validateRegex(const winrt::hstring& regex, IVector<Model::SettingsLoadWarnings>& warnings)
{
    try
    {
        std::wregex{ regex.cbegin(), regex.cend() };
    }
    catch (std::regex_error)
    {
        warnings.Append(Model::SettingsLoadWarnings::InvalidRegex);
    }
}

static void _validateNTMEntries(const IVector<Model::NewTabMenuEntry>& entries, IVector<Model::SettingsLoadWarnings>& warnings)
{
    for (const auto& ntmEntry : entries)
    {
        if (const auto& folderEntry = ntmEntry.try_as<Model::FolderEntry>())
        {
            _validateNTMEntries(folderEntry.RawEntries(), warnings);
        }
        if (const auto& matchProfilesEntry = ntmEntry.try_as<Model::MatchProfilesEntry>())
        {
            if (const auto nameRegex = matchProfilesEntry.Name(); !nameRegex.empty())
            {
                _validateRegex(nameRegex, warnings);
            }
            if (const auto commandlineRegex = matchProfilesEntry.Commandline(); !commandlineRegex.empty())
            {
                _validateRegex(commandlineRegex, warnings);
            }

Review comment (performance):
AGH! STD REGEX MY MORTAL ENEMY! 😄

That said, regex construction can be fairly costly in general, and we should not treat this as some kind of cheap validation. It involves constructing DFAs, etc., after all. I'm not entirely sure anymore where `MatchProfilesEntry` fits into the callstack, but seeing that it's also part of `TerminalSettingsModel`, I'm sure that we can make it cache every regex instance after load and store any warnings into a list. We can then splice its list into the overall `SettingsLoadWarnings` list (or store it directly there, or something like that). Basically, a way to only construct these once.

FWIW if you want to give `icu.h` a try:
```cpp
#include "path/to/buffer/out/UTextAdapter.h"

// Cache this
UErrorCode status = U_ZERO_ERROR;
const auto re = Microsoft::Console::ICU::CreateRegex(pattern, 0, &status);
if (status > U_ZERO_ERROR) {
    // bad regex
}

// Run this
UErrorCode status = U_ZERO_ERROR;
uregex_setText(re.get(), hstring.data(), hstring.size(), &status);
const auto match = uregex_matches(re.get(), 0, &status);
return status == U_ZERO_ERROR && match;
```

Code after:
//   cases
void CascadiaSettings::_validateProfileEnvironmentVariables()
{
    for (const auto& profile : _allProfiles)
    {
        std::set<std::wstring, til::env_key_sorter> envVarNames{};
        if (profile.EnvironmentVariables() == nullptr)
        {
            continue;
        }
        for (const auto [key, value] : profile.EnvironmentVariables())
        {
            const auto iterator = envVarNames.insert(key.c_str());
            if (!iterator.second)
            {
                _warnings.Append(SettingsLoadWarnings::InvalidProfileEnvironmentVariables);
                return;
            }
        }
    }
}

// Returns true if all regexes in the new tab menu are valid, false otherwise
static bool _validateNTMEntries(const IVector<Model::NewTabMenuEntry>& entries)
{
    if (!entries)
    {
        return true;
    }
    for (const auto& ntmEntry : entries)
    {
        if (const auto& folderEntry = ntmEntry.try_as<Model::FolderEntry>())
        {
            if (!_validateNTMEntries(folderEntry.RawEntries()))
            {
                return false;
            }
        }
        if (const auto& matchProfilesEntry = ntmEntry.try_as<Model::MatchProfilesEntry>())
        {
            if (!winrt::get_self<Model::implementation::MatchProfilesEntry>(matchProfilesEntry)->ValidateRegexes())
            {
                return false;
            }
        }
    }
    return true;
}

void CascadiaSettings::_validateRegexes()
{