bfgroup / Lyra

A simple to use, composable, command line parser for C++ 11 and beyond
https://bfgroup.github.io/Lyra/
Boost Software License 1.0
471 stars 56 forks source link

Wrong error message for unrecognized sub-command #74

Open mrubli2 opened 1 year ago

mrubli2 commented 1 year ago

There appears to be a regression in Lyra 1.6 vs. 1.5.1. Take the following parser with a single sub-command named sub:

#include <iostream>
#include <lyra/lyra.hpp>

std::string get_error(const lyra::parse_result& result)
{
    const std::string& error =
#if LYRA_VERSION_MINOR == 5
        result.errorMessage()
#else
        result.message()
#endif
    ;
    return error;
}

int main(int argc, const char **argv)
{
    auto cli = lyra::cli()
        .add_argument(
            lyra::command("sub")
        );

    const auto result = cli.parse({ argc, argv });
    if(!result)
    {
        std::cout << "ERROR: " << get_error(result) << "\n";
        return 1;
    }

    std::cout << "OK\n";
    return 0;
}

With 1.5.1 we get:

$ ./test
OK
$ ./test sub
OK
$ ./test bad
ERROR: Unrecognized token: bad

With 1.6.1 (and the latest develop) however:

$ ./test
OK
$ ./test sub
OK
$ ./test bad
ERROR: Expected: sub

This is quite confusing to users since sub isn't a "required sub-command".

The regression got introduced in 36e22c6a8ff539bdcafbcf2081c377a3f8b6ed98 but that's a fairly big commit and I haven't been able yet to track down the exact spot.

I've also written up a small unit test in case that's helpful:

// unrecognized_command_run_test.cpp

#include <lyra/lyra.hpp>
#include "mini_test.hpp"

bool error_contains(const lyra::parse_result& result, const std::string& expectedError)
{
    const std::string& error =
#if LYRA_VERSION_MINOR == 5
        result.errorMessage()
#else
        result.message()
#endif
    ;
    return error.find(expectedError) != std::string::npos;
}

int main()
{
    using namespace lyra;
    bfg::mini_test::scope test;

    auto cli = lyra::cli()
        .add_argument(lyra::command("sub"));
    {
        const auto result = cli.parse( { "TestApp" } );
        test
            (REQUIRE( result ))
        ;
    }
    {
        const auto result = cli.parse( { "TestApp", "sub" } );
        test
            (REQUIRE( result ))
        ;
    }
    {
        const auto result = cli.parse( { "TestApp", "bad" } );
        test
            (REQUIRE( !result ))
            (REQUIRE( error_contains(result, "Unrecognized token") ))
        ;
    }

    return test;
}