diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index cea5c7e1e..e365f9c9d 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -497,11 +497,11 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv) { - CommandLineParser parser; + CommandLineParser parser(sout(false), serr(false)); bool success = parser.parse(_argc, _argv, isatty(fileno(stdin))); if (!success) return false; - g_hasOutput = g_hasOutput || CommandLineParser::hasOutput(); + g_hasOutput = g_hasOutput || parser.hasOutput(); m_options = parser.options(); return true; diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 263637907..409648778 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -34,24 +34,16 @@ namespace po = boost::program_options; namespace solidity::frontend { -static bool g_hasOutput = false; - -namespace +ostream& CommandLineParser::sout() { - -std::ostream& sout() -{ - g_hasOutput = true; - return cout; + m_hasOutput = true; + return m_sout; } -std::ostream& serr(bool _used = true) +ostream& CommandLineParser::serr() { - if (_used) - g_hasOutput = true; - return cerr; -} - + m_hasOutput = true; + return m_serr; } #define cout @@ -233,8 +225,7 @@ static set const g_metadataHashArgs g_strNone }; -[[noreturn]] -static void printVersionAndExit() +void CommandLineParser::printVersionAndExit() { sout() << "solc, the solidity compiler commandline interface" << @@ -245,8 +236,7 @@ static void printVersionAndExit() exit(EXIT_SUCCESS); } -[[noreturn]] -static void printLicenseAndExit() +void CommandLineParser::printLicenseAndExit() { sout() << otherLicenses << endl; // This is a static variable generated by cmake from LICENSE.txt @@ -254,10 +244,8 @@ static void printLicenseAndExit() exit(EXIT_SUCCESS); } -namespace -{ -bool checkMutuallyExclusive(boost::program_options::variables_map const& args, std::string const& _optionA, std::string const& _optionB) +bool CommandLineParser::checkMutuallyExclusive(boost::program_options::variables_map const& args, string const& _optionA, string const& _optionB) { if (args.count(_optionA) && args.count(_optionB)) { @@ -268,8 +256,6 @@ bool checkMutuallyExclusive(boost::program_options::variables_map const& args, s return true; } -} - bool CompilerOutputs::operator==(CompilerOutputs const& _other) const noexcept { static_assert( @@ -489,7 +475,7 @@ bool CommandLineParser::parseLibraryOption(string const& _input) bool CommandLineParser::parse(int _argc, char const* const* _argv, bool interactiveTerminal) { - g_hasOutput = false; + m_hasOutput = false; // Declare the supported options. po::options_description desc((R"(solc, the Solidity commandline compiler. @@ -1186,11 +1172,6 @@ General Information)").c_str(), return true; } -bool CommandLineParser::hasOutput() -{ - return g_hasOutput; -} - bool CommandLineParser::parseCombinedJsonOption() { if (!m_args.count(g_argCombinedJson)) @@ -1242,4 +1223,5 @@ string CommandLineParser::joinOptionNames(vector const& _optionNames, st _separator ); } + } // namespace solidity::frontend diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 7d7223419..b05caa705 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -177,6 +178,11 @@ struct CommandLineOptions class CommandLineParser { public: + explicit CommandLineParser(std::ostream& _sout, std::ostream& _serr): + m_sout(_sout), + m_serr(_serr) + {} + /// Parses the command-line arguments and fills out the internal CommandLineOptions structure. /// Performs validation and prints error messages. If requested, prints usage banner, version /// or license. @@ -191,9 +197,8 @@ public: CommandLineOptions const& options() const { return m_options; } - /// Returns true if any parser instance has written anything to cout or cerr since the last - /// call to parse(). - static bool hasOutput(); + /// Returns true if the parser has written anything to any of its output streams. + bool hasOutput() const { return m_hasOutput; } private: /// Parses the value supplied to --combined-json. @@ -210,9 +215,28 @@ private: /// @return false if there are any validation errors, true otherwise. bool parseLibraryOption(std::string const& _input); + bool checkMutuallyExclusive( + boost::program_options::variables_map const& args, + std::string const& _optionA, + std::string const& _optionB + ); + [[noreturn]] void printVersionAndExit(); + [[noreturn]] void printLicenseAndExit(); size_t countEnabledOptions(std::vector const& _optionNames) const; static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); + /// Returns the stream that should receive normal output. Sets m_hasOutput to true if the + /// stream has ever been used. + std::ostream& sout(); + + /// Returns the stream that should receive error output. Sets m_hasOutput to true if the + /// stream has ever been used. + std::ostream& serr(); + + std::ostream& m_sout; + std::ostream& m_serr; + bool m_hasOutput = false; + CommandLineOptions m_options; /// Map of command-line arguments produced by boost::program_options. diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index ece4710b0..a13fd0ccd 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -46,7 +46,7 @@ using namespace solidity::yul; namespace { -optional parseCommandLine(vector const& commandLine) +optional parseCommandLine(vector const& commandLine, ostream& _stdout, ostream& _stderr) { size_t argc = commandLine.size(); vector argv(argc + 1); @@ -57,7 +57,7 @@ optional parseCommandLine(vector const& commandLine) for (size_t i = 0; i < argc; ++i) argv[i] = commandLine[i].c_str(); - CommandLineParser cliParser; + CommandLineParser cliParser(_stdout, _stderr); bool success = cliParser.parse( static_cast(argc), argv.data(), @@ -93,8 +93,11 @@ BOOST_AUTO_TEST_CASE(no_options) nullopt, }; - optional parsedOptions = parseCommandLine(commandLine); + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); BOOST_TEST((parsedOptions.value() == expectedOptions)); } @@ -202,8 +205,11 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) 5, }; - optional parsedOptions = parseCommandLine(commandLine); + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); BOOST_TEST((parsedOptions.value() == expectedOptions)); } @@ -316,8 +322,11 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.optimizer.yulSteps = "agf"; } - optional parsedOptions = parseCommandLine(commandLine); + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == "Warning: Yul is still experimental. Please use the output with care.\n"); BOOST_REQUIRE(parsedOptions.has_value()); BOOST_TEST((parsedOptions.value() == expectedOptions)); @@ -388,8 +397,11 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) expectedOptions.compiler.combinedJsonRequests->abi = true; expectedOptions.compiler.combinedJsonRequests->binary = true; - optional parsedOptions = parseCommandLine(commandLine); + stringstream sout, serr; + optional parsedOptions = parseCommandLine(commandLine, sout, serr); + BOOST_TEST(sout.str() == ""); + BOOST_TEST(serr.str() == ""); BOOST_REQUIRE(parsedOptions.has_value()); BOOST_TEST((parsedOptions.value() == expectedOptions)); }