[isoltest] Add support for external sources.

This commit is contained in:
Alexander Arlt 2021-03-15 17:52:24 -05:00
parent 2969bc0f3e
commit 481971cbcf
48 changed files with 328 additions and 68 deletions

View File

@ -697,15 +697,15 @@ vector<string> CompilerStack::contractNames() const
return contractNames;
}
string const CompilerStack::lastContractName() const
string const CompilerStack::lastContractName(optional<string> const& _sourceName) const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
// try to find some user-supplied contract
string contractName;
for (auto const& it: m_sources)
for (ASTPointer<ASTNode> const& node: it.second.ast->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
if (_sourceName.value_or(it.first) == it.first)
for (auto const* contract: ASTNode::filteredNodes<ContractDefinition>(it.second.ast->nodes()))
contractName = contract->fullyQualifiedName();
return contractName;
}

View File

@ -261,8 +261,8 @@ public:
/// @returns a list of the contract names in the sources.
std::vector<std::string> contractNames() const;
/// @returns the name of the last contract.
std::string const lastContractName() const;
/// @returns the name of the last contract. If _sourceName is defined the last contract of that source will be returned.
std::string const lastContractName(std::optional<std::string> const& _sourceName = std::nullopt) const;
/// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use
std::string const filesystemFriendlyName(std::string const& _contractName) const;

View File

@ -21,6 +21,7 @@
#include <test/Common.h>
#include <libsolutil/Assertions.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
@ -222,4 +223,19 @@ void CommonOptions::setSingleton(std::unique_ptr<CommonOptions const>&& _instanc
std::unique_ptr<CommonOptions const> CommonOptions::m_singleton = nullptr;
bool isValidSemanticTestPath(boost::filesystem::path const& _testPath)
{
bool insideSemanticTests = false;
fs::path testPathPrefix;
for (auto const& element: _testPath)
{
testPathPrefix /= element;
if (boost::ends_with(canonical(testPathPrefix).generic_string(), "/test/libsolidity/semanticTests"))
insideSemanticTests = true;
if (insideSemanticTests && boost::starts_with(element.string(), "_"))
return false;
}
return true;
}
}

View File

@ -86,4 +86,9 @@ private:
static std::unique_ptr<CommonOptions const> m_singleton;
};
/// @return true if it is ok to treat the file located under the specified path as a semantic test.
/// I.e. if the test is located in the semantic test directory and is not excluded due to being a part of external sources.
/// Note: @p _testPath can be relative but must include at least the `/test/libsolidity/semanticTests/` part
bool isValidSemanticTestPath(boost::filesystem::path const& _testPath);
}

View File

@ -59,7 +59,7 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end)
CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion):
EVMVersionRestrictedTestCase(_filename),
m_sources(m_reader.sources().sources),
m_sources(m_reader.sources()),
m_expectations(parseExpectations(m_reader.stream())),
m_evmVersion(_evmVersion)
{
@ -92,12 +92,13 @@ void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const&
void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const
{
if (m_sources.empty())
if (m_sources.sources.empty())
return;
bool outputSourceNames = (m_sources.size() != 1 || !m_sources.begin()->first.empty());
assert(m_sources.externalSources.empty());
bool outputSourceNames = (m_sources.sources.size() != 1 || !m_sources.sources.begin()->first.empty());
for (auto const& [name, source]: m_sources)
for (auto const& [name, source]: m_sources.sources)
if (_formatted)
{
if (source.empty())

View File

@ -20,6 +20,7 @@
#include <test/libsolidity/AnalysisFramework.h>
#include <test/TestCase.h>
#include <test/TestCaseReader.h>
#include <liblangutil/Exceptions.h>
#include <libsolutil/AnsiColorized.h>
@ -81,7 +82,7 @@ protected:
static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream);
std::map<std::string, std::string> m_sources;
frontend::test::SourceMap m_sources;
std::vector<SyntaxTestError> m_expectations;
std::vector<SyntaxTestError> m_errorList;
langutil::EVMVersion const m_evmVersion;

View File

@ -60,7 +60,8 @@ public:
u256 const& _value = 0,
std::string const& _contractName = "",
bytes const& _arguments = {},
std::map<std::string, util::h160> const& _libraryAddresses = {}
std::map<std::string, util::h160> const& _libraryAddresses = {},
std::optional<std::string> const& _sourceName = std::nullopt
) = 0;
bytes const& compileAndRun(

View File

@ -18,24 +18,29 @@
#include <test/TestCaseReader.h>
#include <libsolidity/parsing/Parser.h>
#include <libsolutil/StringUtils.h>
#include <libsolutil/CommonIO.h>
#include <boost/algorithm/string.hpp>
#include <boost/throw_exception.hpp>
#include <boost/filesystem.hpp>
#include <range/v3/view/map.hpp>
using namespace std;
using namespace solidity::langutil;
using namespace solidity::frontend::test;
TestCaseReader::TestCaseReader(string const& _filename):
m_file(_filename)
{
if (!m_file)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\"."));
m_file.exceptions(ios::badbit);
namespace fs = boost::filesystem;
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_file);
TestCaseReader::TestCaseReader(string const& _filename): m_fileStream(_filename), m_fileName(_filename)
{
if (!m_fileStream)
BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\"."));
m_fileStream.exceptions(ios::badbit);
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_fileStream);
m_unreadSettings = m_settings;
}
@ -55,7 +60,7 @@ string const& TestCaseReader::source() const
string TestCaseReader::simpleExpectations()
{
return parseSimpleExpectations(m_file);
return parseSimpleExpectations(m_fileStream);
}
bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue)
@ -105,10 +110,12 @@ void TestCaseReader::ensureAllSettingsRead() const
pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream)
{
map<string, string> sources;
map<string, boost::filesystem::path> externalSources;
string currentSourceName;
string currentSource;
string line;
size_t lineNumber = 1;
static string const externalSourceDelimiterStart("==== ExternalSource:");
static string const sourceDelimiterStart("==== Source:");
static string const sourceDelimiterEnd("====");
static string const comment("// ");
@ -137,6 +144,41 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
if (sources.count(currentSourceName))
BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + currentSourceName + "\"."));
}
else if (boost::algorithm::starts_with(line, externalSourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd))
{
string externalSourceString = boost::trim_copy(line.substr(
externalSourceDelimiterStart.size(),
line.size() - sourceDelimiterEnd.size() - externalSourceDelimiterStart.size()
));
string externalSourceName;
size_t remappingPos = externalSourceString.find('=');
// Does the external source define a remapping?
if (remappingPos != string::npos)
{
externalSourceName = boost::trim_copy(externalSourceString.substr(0, remappingPos));
externalSourceString = boost::trim_copy(externalSourceString.substr(remappingPos + 1));
}
else
externalSourceName = externalSourceString;
solAssert(!externalSourceName.empty(), "");
fs::path externalSourceTarget(externalSourceString);
fs::path testCaseParentDir = m_fileName.parent_path();
if (!externalSourceTarget.is_relative())
BOOST_THROW_EXCEPTION(runtime_error("External Source paths need to be relative to the location of the test case."));
fs::path externalSourceFullPath = testCaseParentDir / externalSourceTarget;
string externalSourceContent;
if (!fs::exists(externalSourceFullPath))
BOOST_THROW_EXCEPTION(runtime_error("External Source '" + externalSourceTarget.string() + "' not found."));
else
externalSourceContent = util::readFileAsString(externalSourceFullPath.string());
if (sources.count(externalSourceName))
BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + externalSourceName + "\"."));
sources[externalSourceName] = externalSourceContent;
externalSources[externalSourceName] = externalSourceTarget;
}
else
currentSource += line + "\n";
}
@ -156,7 +198,7 @@ pair<SourceMap, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(is
}
// Register the last source as the main one
sources[currentSourceName] = currentSource;
return {{move(sources), move(currentSourceName)}, lineNumber};
return {{move(sources), move(externalSources), move(currentSourceName)}, lineNumber};
}
string TestCaseReader::parseSimpleExpectations(istream& _file)

View File

@ -16,6 +16,7 @@
*/
// SPDX-License-Identifier: GPL-3.0
#include <boost/filesystem.hpp>
#include <fstream>
#include <map>
#include <string>
@ -31,6 +32,7 @@ namespace solidity::frontend::test
struct SourceMap
{
std::map<std::string, std::string> sources;
std::map<std::string, boost::filesystem::path> externalSources;
std::string mainSourceFile;
};
@ -48,7 +50,7 @@ public:
std::string const& source() const;
std::size_t lineNumber() const { return m_lineNumber; }
std::map<std::string, std::string> const& settings() const { return m_settings; }
std::ifstream& stream() { return m_file; }
std::ifstream& stream() { return m_fileStream; }
std::string simpleExpectations();
@ -62,7 +64,8 @@ private:
std::pair<SourceMap, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file);
static std::string parseSimpleExpectations(std::istream& _file);
std::ifstream m_file;
std::ifstream m_fileStream;
boost::filesystem::path m_fileName;
SourceMap m_sources;
std::size_t m_lineNumber = 0;
std::map<std::string, std::string> m_settings;

View File

@ -86,7 +86,10 @@ int registerTests(
fs::directory_iterator(fullpath),
fs::directory_iterator()
))
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
if (
solidity::test::isValidSemanticTestPath(entry) &&
(fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
)
numTestsAdded += registerTests(
*sub_suite,
_basepath, _path / entry.path().filename(),

View File

@ -256,7 +256,7 @@ TestCase::TestResult SemanticTest::runTest(
{
soltestAssert(
m_allowNonExistingFunctions ||
m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
m_compiler.methodIdentifiers(m_compiler.lastContractName(m_sources.mainSourceFile)).isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler"
);
@ -283,7 +283,7 @@ TestCase::TestResult SemanticTest::runTest(
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(std::move(output));
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName(m_sources.mainSourceFile)));
}
}
@ -379,18 +379,37 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
if (m_sources.sources.empty())
return;
bool outputNames = (m_sources.sources.size() != 1 || !m_sources.sources.begin()->first.empty());
bool outputNames = (m_sources.sources.size() - m_sources.externalSources.size() != 1 || !m_sources.sources.begin()->first.empty());
set<string> externals;
for (auto const& [name, path]: m_sources.externalSources)
{
externals.insert(name);
string externalSource;
if (name == path)
externalSource = name;
else
externalSource = name + "=" + path.generic_string();
if (_formatted)
_stream << _linePrefix << formatting::CYAN << "==== ExternalSource: " << externalSource << " ===="s << formatting::RESET << endl;
else
_stream << _linePrefix << "==== ExternalSource: " << externalSource << " ===="s << endl;
}
for (auto const& [name, source]: m_sources.sources)
if (externals.find(name) == externals.end())
{
if (_formatted)
{
if (source.empty())
continue;
if (outputNames)
_stream << _linePrefix << formatting::CYAN << "==== Source: " << name << " ====" << formatting::RESET << endl;
vector<char const*> sourceFormatting(source.length(), formatting::RESET);
_stream << _linePrefix << formatting::CYAN << "==== Source: " << name
<< " ====" << formatting::RESET << endl;
vector<char const*> sourceFormatting(source.length(), formatting::RESET);
_stream << _linePrefix << sourceFormatting.front() << source.front();
for (size_t i = 1; i < source.length(); i++)
{
@ -416,6 +435,7 @@ void SemanticTest::printSource(ostream& _stream, string const& _linePrefix, bool
while (getline(stream, line))
_stream << _linePrefix << line << endl;
}
}
}
void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) const
@ -455,6 +475,6 @@ bool SemanticTest::deploy(
map<string, solidity::test::Address> const& _libraries
)
{
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries);
auto output = compileAndRunWithoutCheck(m_sources.sources, _value, _contractName, _arguments, _libraries, m_sources.mainSourceFile);
return !output.empty() && m_transactionSuccessful;
}

View File

@ -36,10 +36,13 @@ using namespace std;
bytes SolidityExecutionFramework::multiSourceCompileContract(
map<string, string> const& _sourceCode,
optional<string> const& _mainSourceName,
string const& _contractName,
map<string, Address> const& _libraryAddresses
)
{
if (_mainSourceName.has_value())
solAssert(_sourceCode.find(_mainSourceName.value()) != _sourceCode.end(), "");
map<string, string> sourcesWithPreamble = _sourceCode;
for (auto& entry: sourcesWithPreamble)
entry.second = addPreamble(entry.second);
@ -68,7 +71,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
formatter.printErrorInformation(*error);
BOOST_ERROR("Compiling contract failed");
}
std::string contractName(_contractName.empty() ? m_compiler.lastContractName() : _contractName);
string contractName(_contractName.empty() ? m_compiler.lastContractName(_mainSourceName) : _contractName);
evmasm::LinkerObject obj;
if (m_compileViaYul)
{
@ -98,7 +101,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
try
{
asmStack.optimize();
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
obj = move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
obj.link(_libraryAddresses);
break;
}
@ -126,6 +129,7 @@ bytes SolidityExecutionFramework::compileContract(
{
return multiSourceCompileContract(
{{"", _sourceCode}},
nullopt,
_contractName,
_libraryAddresses
);

View File

@ -49,10 +49,11 @@ public:
u256 const& _value = 0,
std::string const& _contractName = "",
bytes const& _arguments = {},
std::map<std::string, solidity::test::Address> const& _libraryAddresses = {}
std::map<std::string, solidity::test::Address> const& _libraryAddresses = {},
std::optional<std::string> const& _sourceName = std::nullopt
) override
{
bytes bytecode = multiSourceCompileContract(_sourceCode, _contractName, _libraryAddresses);
bytes bytecode = multiSourceCompileContract(_sourceCode, _sourceName, _contractName, _libraryAddresses);
sendMessage(bytecode + _arguments, true, _value);
return m_output;
}
@ -65,6 +66,7 @@ public:
bytes multiSourceCompileContract(
std::map<std::string, std::string> const& _sources,
std::optional<std::string> const& _mainSourceName = std::nullopt,
std::string const& _contractName = "",
std::map<std::string, solidity::test::Address> const& _libraryAddresses = {}
);

View File

@ -65,7 +65,7 @@ string SyntaxTest::addPreamble(string const& _sourceCode)
void SyntaxTest::setupCompiler()
{
compiler().reset();
auto sourcesWithPragma = m_sources;
auto sourcesWithPragma = m_sources.sources;
for (auto& source: sourcesWithPragma)
source.second = addPreamble(source.second);
compiler().setSources(sourcesWithPragma);
@ -122,8 +122,8 @@ void SyntaxTest::filterObtainedErrors()
solAssert(location->source, "");
sourceName = location->source->name();
solAssert(m_sources.count(sourceName) == 1, "");
int preambleSize = static_cast<int>(location->source->source().size()) - static_cast<int>(m_sources[sourceName].size());
solAssert(m_sources.sources.count(sourceName) == 1, "");
int preambleSize = static_cast<int>(location->source->source().size()) - static_cast<int>(m_sources.sources[sourceName].size());
solAssert(preambleSize >= 0, "");
// ignore the version & license pragma inserted by the testing tool when calculating locations.

View File

@ -0,0 +1,2 @@
contract External {
}

View File

@ -0,0 +1,2 @@
contract External {
}

View File

@ -0,0 +1,2 @@
import "external.sol";
import "other_external.sol";

View File

@ -0,0 +1 @@
import "subdir/import.sol";

View File

@ -0,0 +1,2 @@
contract OtherExternal {
}

View File

@ -0,0 +1 @@
import "sub_external.sol";

View File

@ -0,0 +1,2 @@
contract SubExternal {
}

View File

@ -0,0 +1,2 @@
contract A {
}

View File

@ -0,0 +1,2 @@
contract C {
}

View File

@ -0,0 +1,2 @@
contract D {
}

View File

@ -0,0 +1,2 @@
contract D {
}

View File

@ -0,0 +1,2 @@
contract C {
}

View File

@ -0,0 +1,3 @@
import {C} from "../../c.sol";
contract B {
}

View File

@ -0,0 +1,3 @@
import {B} from "../B/b.sol";
contract G {
}

View File

@ -0,0 +1,2 @@
contract A {
}

View File

@ -0,0 +1,8 @@
import {A} from "./a.sol";
import {B} from "./B/b.sol";
import {C} from "../c.sol";
import {D} from "../D/d.sol";
import {G} from "./E/../F/../G/./g.sol";
import {H} from "../../../../_relative_imports/h.sol";
contract Contract {
}

View File

@ -0,0 +1,2 @@
contract H {
}

View File

@ -0,0 +1,2 @@
contract B {
}

View File

@ -0,0 +1,2 @@
import {A} from "./a.sol";
import {B} from "../b.sol";

View File

@ -0,0 +1,2 @@
contract Dot_A {
}

View File

@ -0,0 +1,2 @@
contract Dot_Dot_B {
}

View File

@ -0,0 +1,8 @@
==== ExternalSource: a=_external/external.sol=sol ====
import {External} from "a";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,10 @@
==== ExternalSource: _external/external.sol ====
==== ExternalSource: _external/other_external.sol ====
import {External} from "_external/external.sol";
import {OtherExternal} from "_external/other_external.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,14 @@
==== ExternalSource: _external/external.sol ====
==== Source: s1.sol ====
import {External} from "_external/external.sol";
contract S1 {
}
==== Source: s2.sol ====
import {S1} from "s1.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,12 @@
==== ExternalSource: _non_normalized_paths//a.sol ====
==== ExternalSource: C/////c.sol=_non_normalized_paths/c.sol ====
==== ExternalSource: C/../////D/d.sol=_non_normalized_paths///d.sol ====
import {A} from "_non_normalized_paths//a.sol";
import {C} from "C/////c.sol";
import {D} from "C/../////D/d.sol";
contract Contract {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,14 @@
==== ExternalSource: _relative_imports/dir/contract.sol ====
==== ExternalSource: _relative_imports/dir/a.sol ====
==== ExternalSource: _relative_imports/dir/B/b.sol ====
==== ExternalSource: _relative_imports/c.sol ====
==== ExternalSource: _relative_imports/D/d.sol ====
==== ExternalSource: _relative_imports/dir/G/g.sol ====
==== ExternalSource: _relative_imports/h.sol ====
import {A, B, C, D, G, H, Contract} from "_relative_imports/dir/contract.sol";
contract CC {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,8 @@
==== ExternalSource: _external/external.sol ====
import {External} from "_external/external.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,10 @@
==== ExternalSource: _external/external.sol ====
==== ExternalSource: _external/other_external.sol ====
import {External} from "_external/external.sol";
import {OtherExternal} from "_external/other_external.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,10 @@
==== ExternalSource: _external/import_with_subdir.sol ====
==== ExternalSource: subdir/import.sol=_external/subdir/import.sol ====
==== ExternalSource: sub_external.sol=_external/subdir/sub_external.sol ====
import {SubExternal} from "sub_external.sol";
contract C {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,12 @@
==== ExternalSource: ./a.sol=_source_name_starting_with_dots/dot_a.sol ====
==== ExternalSource: ../b.sol=_source_name_starting_with_dots/dot_dot_b.sol ====
==== ExternalSource: _source_name_starting_with_dots/dir/a.sol ====
==== ExternalSource: _source_name_starting_with_dots/b.sol ====
==== ExternalSource: _source_name_starting_with_dots/dir/contract.sol ====
import {A, B} from "_source_name_starting_with_dots/dir/contract.sol";
contract Contract {
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -0,0 +1,12 @@
==== ExternalSource: ExtSource.sol=_external/external.sol ====
==== ExternalSource: /ExtSource.sol=_external/other_external.sol ====
import "ExtSource.sol";
import "/ExtSource.sol";
contract C {
External _external;
OtherExternal _otherExternal;
}
// ====
// compileViaYul: also
// ----
// constructor()

View File

@ -25,6 +25,7 @@
#include <test/libyul/Common.h>
#include <test/libyul/SyntaxTest.h>
#include <test/TestCaseReader.h>
#include <test/libsolidity/util/SoltestErrors.h>
@ -35,14 +36,15 @@ using namespace solidity;
using namespace solidity::util;
using namespace solidity::langutil;
using namespace solidity::yul::test;
using namespace solidity::frontend::test;
void SyntaxTest::parseAndAnalyze()
{
if (m_sources.size() != 1)
if (m_sources.sources.size() != 1)
BOOST_THROW_EXCEPTION(runtime_error{"Expected only one source for yul test."});
string const& name = m_sources.begin()->first;
string const& source = m_sources.begin()->second;
string const& name = m_sources.sources.begin()->first;
string const& source = m_sources.sources.begin()->second;
ErrorList errorList{};
soltestAssert(m_dialect, "");

View File

@ -79,9 +79,9 @@ public:
m_filterExpression = regex{"(" + filter + "(\\.sol|\\.yul))"};
}
bool matches(string const& _name) const
bool matches(fs::path const& _path, string const& _name) const
{
return regex_match(_name, m_filterExpression);
return regex_match(_name, m_filterExpression) && solidity::test::isValidSemanticTestPath(_path);
}
private:
@ -154,7 +154,7 @@ TestTool::Result TestTool::process()
try
{
if (m_filter.matches(m_name))
if (m_filter.matches(m_path, m_name))
{
(AnsiColorized(cout, formatted, {BOLD}) << m_name << ": ").flush();