Skip to content

Commit

Permalink
refactor: tool uses Expected
Browse files Browse the repository at this point in the history
  • Loading branch information
alandefreitas committed Dec 1, 2023
1 parent 1a1b510 commit 3e1c279
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 119 deletions.
116 changes: 109 additions & 7 deletions include/mrdocs/Support/Error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,25 +515,97 @@ class Unexpected

template <class E> Unexpected(E) -> Unexpected<E>;

namespace detail
{
template <class T>
constexpr
bool
failed(T const&t)
{
if constexpr (isExpected<std::decay_t<T>>)
{
return !t;
}
else if constexpr (std::same_as<std::decay_t<T>, Error>)
{
return t.failed();
}
else if constexpr (requires (T const& t0) { t0.empty(); })
{
return t.empty();
}
else if constexpr (std::constructible_from<bool, T>)
{
return !t;
}
}

template <class T>
constexpr
decltype(auto)
error(T const& t)
{
if constexpr (isExpected<std::decay_t<T>>)
{
return t.error();
}
else if constexpr (std::same_as<std::decay_t<T>, Error>)
{
return t;
}
else if constexpr (requires (T const& t0) { t0.empty(); })
{
return Error("Empty value");
}
else if constexpr (std::constructible_from<bool, T>)
{
return Error("Invalid value");
}
}
}

#ifndef MRDOCS_TRY
# define MRDOCS_MERGE_(a, b) a##b
# define MRDOCS_LABEL_(a) MRDOCS_MERGE_(expected_result_, a)
# define MRDOCS_UNIQUE_NAME MRDOCS_LABEL_(__LINE__)

/// Try to retrive expected-like type
# define MRDOCS_TRY_VOID(expr) \
auto MRDOCS_UNIQUE_NAME = expr; \
if (!MRDOCS_UNIQUE_NAME) { \
return Unexpected(MRDOCS_UNIQUE_NAME.error()); \
if (detail::failed(MRDOCS_UNIQUE_NAME)) { \
return Unexpected(detail::error(MRDOCS_UNIQUE_NAME)); \
} \
void(0)
# define MRDOCS_TRY_VAR(var, expr) \
auto MRDOCS_UNIQUE_NAME = expr; \
if (!MRDOCS_UNIQUE_NAME) { \
return Unexpected(MRDOCS_UNIQUE_NAME.error()); \
} \
if (detail::failed(MRDOCS_UNIQUE_NAME)) { \
return Unexpected(detail::error(MRDOCS_UNIQUE_NAME)); \
} \
var = *std::move(MRDOCS_UNIQUE_NAME)
# define GET_MACRO(_1, _2, NAME, ...) NAME
# define MRDOCS_TRY_MSG(var, expr, msg) \
auto MRDOCS_UNIQUE_NAME = expr; \
if (detail::failed(MRDOCS_UNIQUE_NAME)) { \
return Unexpected(Error(msg)); \
} \
var = *std::move(MRDOCS_UNIQUE_NAME)
# define MRDOCS_TRY_GET_MACRO(_1, _2, _3, NAME, ...) NAME
# define MRDOCS_TRY(...) \
GET_MACRO(__VA_ARGS__, MRDOCS_TRY_VAR, MRDOCS_TRY_VOID)(__VA_ARGS__)
MRDOCS_TRY_GET_MACRO(__VA_ARGS__, MRDOCS_TRY_MSG, MRDOCS_TRY_VAR, MRDOCS_TRY_VOID)(__VA_ARGS__)

/// Check existing expected-like type
# define MRDOCS_CHECK_VOID(var) \
if (!detail::failed(var)) { \
return Unexpected(detail::error(var)); \
} \
void(0)
# define MRDOCS_CHECK_MSG(var, msg) \
if (detail::failed(var)) { \
return Unexpected(Error(msg)); \
} \
void(0)
# define MRDOCS_CHECK_GET_MACRO(_1, _2, NAME, ...) NAME
# define MRDOCS_CHECK(...) \
MRDOCS_CHECK_GET_MACRO(__VA_ARGS__, MRDOCS_CHECK_MSG, MRDOCS_CHECK_VOID)(__VA_ARGS__)
#endif


Expand Down Expand Up @@ -1854,6 +1926,36 @@ class Expected<T, E>
: unex_(std::move(u).error()), has_value_(false)
{ }

// The following constructors are extensions that allow
// Expected to be constructed directly from an error type
// if this is not ambiguous with the value type. This
// is not part of std::expected. MRDOCS_EXPECTED_FROM_ERROR
// can be defined to disable this behavior.
#ifndef MRDOCS_EXPECTED_FROM_ERROR
template <class G = E>
requires
std::is_constructible_v<E, const G&> &&
(!std::is_constructible_v<T, const G&>)
constexpr
explicit(!std::is_convertible_v<const G&, E>)
Expected(const G& u)
noexcept(std::is_nothrow_constructible_v<E, const G&>)
: unex_(u)
, has_value_(false)
{ }

template <class G = E>
requires
std::is_constructible_v<E, G> &&
(!std::is_constructible_v<T, G>)
constexpr
explicit(!std::is_convertible_v<G, E>)
Expected(G&& u)
noexcept(std::is_nothrow_constructible_v<E, G>)
: unex_(std::move(u)), has_value_(false)
{ }
#endif

constexpr explicit
Expected(std::in_place_t) noexcept
: Expected()
Expand Down
2 changes: 2 additions & 0 deletions include/mrdocs/Support/Path.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ isDirsy(
@li Separators made uniform
@li Separators are replaced with the native separator
@return The normalized path.
@param pathName The relative or absolute path.
Expand Down
4 changes: 2 additions & 2 deletions src/lib/Support/Path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ makeDirsy(
namespace path = llvm::sys::path;

std::string result = static_cast<std::string>(pathName);
if( ! result.empty() &&
! path::is_separator(
if (!result.empty() &&
!path::is_separator(
result.back(),
path::Style::windows_slash))
{
Expand Down
74 changes: 31 additions & 43 deletions src/tool/Addons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,59 +17,47 @@
namespace clang {
namespace mrdocs {

Error
Expected<void>
setupAddonsDir(
llvm::cl::opt<std::string>& addonsDirArg,
char const* argv0,
void* addressOfMain)
{
namespace fs = llvm::sys::fs;
using Process = llvm::sys::Process;

std::string addonsDir;

// Set addons dir
if(! addonsDirArg.getValue().empty())
// Set addons dir from command line argument
if (!addonsDirArg.getValue().empty())
{
// from command line
auto absPath = files::makeAbsolute(
addonsDirArg.getValue());
if(! absPath)
return absPath.error();
addonsDir = files::makeDirsy(files::normalizePath(*absPath));
if(auto err = files::requireDirectory(addonsDir))
return err;
MRDOCS_TRY(
std::string absPath,
files::makeAbsolute(addonsDirArg.getValue()));
std::string addonsDir = files::makeDirsy(
files::normalizePath(absPath));
MRDOCS_TRY(files::requireDirectory(addonsDir));
addonsDirArg.getValue() = addonsDir;
return {};
}
else
{
// check process working directory
addonsDir = fs::getMainExecutable(argv0, addressOfMain);
if(addonsDir.empty())
return Error("getMainExecutable failed");
addonsDir = files::makeDirsy(files::appendPath(
files::getParentDir(addonsDir), "addons"));
if(! files::requireDirectory(addonsDir).failed())
{
// from directory containing the process executable
addonsDirArg.getValue() = addonsDir;
}
else
{
auto addonsEnvVar = Process::GetEnv("MRDOCS_ADDONS_DIR");
if(! addonsEnvVar.has_value())
return Error("no MRDOCS_ADDONS_DIR in environment");

// from environment variable
addonsDir = files::makeDirsy(files::normalizePath(*addonsEnvVar));
if(auto err = files::requireAbsolute(addonsDir))
return err;
if(auto err = files::requireDirectory(addonsDir))
return err;
addonsDirArg.getValue() = addonsDir;
}
// Set addons dir from process working directory
std::string addonsDir = llvm::sys::fs::getMainExecutable(argv0, addressOfMain);
MRDOCS_CHECK(addonsDir, "getMainExecutable failed");
addonsDir = files::makeDirsy(files::appendPath(
files::getParentDir(addonsDir), "addons"));
Error err = files::requireDirectory(addonsDir);
if (!err.failed())
{
addonsDirArg.getValue() = addonsDir;
return {};
}
return Error::success();

// Set addons dir from environment variable
MRDOCS_TRY(
std::string addonsEnvVar,
llvm::sys::Process::GetEnv("MRDOCS_ADDONS_DIR"),
"no MRDOCS_ADDONS_DIR in environment");
addonsDir = files::makeDirsy(files::normalizePath(addonsEnvVar));
MRDOCS_TRY(files::requireAbsolute(addonsDir));
MRDOCS_TRY(files::requireDirectory(addonsDir));
addonsDirArg.getValue() = addonsDir;
return {};
}

} // mrdocs
Expand Down
4 changes: 3 additions & 1 deletion src/tool/Addons.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ namespace mrdocs {

/** Set the addons directory using the argument as a hint.
@return The error if any occurred.
*/
Error
Expected<void>
setupAddonsDir(
llvm::cl::opt<std::string>& addonsDirArg,
char const* argv0,
Expand Down
88 changes: 43 additions & 45 deletions src/tool/GenerateAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,83 +22,81 @@
namespace clang {
namespace mrdocs {

Error
Expected<void>
DoGenerateAction()
{
auto& generators = getGenerators();

ThreadPool threadPool(toolArgs.concurrency);

// Calculate additional YAML settings from command line options.
// Get additional YAML settings from command line
std::string extraYaml;
{
llvm::raw_string_ostream os(extraYaml);
if(toolArgs.ignoreMappingFailures.getValue())
if (toolArgs.ignoreMappingFailures.getValue())
{
os << "ignore-failures: true\n";
}
}

// Load YAML configuration file
std::shared_ptr<ConfigImpl const> config;
ThreadPool threadPool(toolArgs.concurrency);
{
// Load configuration file
if(toolArgs.configPath.empty())
return formatError("the config path argument is missing");
auto configFile = loadConfigFile(
MRDOCS_CHECK(toolArgs.configPath, "The config path argument is missing");
MRDOCS_TRY(auto configFile, loadConfigFile(
toolArgs.configPath,
toolArgs.addonsDir,
extraYaml,
nullptr,
threadPool);
if(! configFile)
return configFile.error();

config = std::move(configFile.value());
threadPool));
config = std::move(configFile);
}


// Create the generator
auto generator = generators.find(config->settings().generate);
if(! generator)
return formatError("the Generator \"{}\" was not found",
config->settings().generate);
// Get the generator
MRDOCS_TRY(
Generator const& generator,
getGenerators().find(config->settings().generate),
formatError(
"the Generator \"{}\" was not found",
config->settings().generate));

// Load the compilation database
if(toolArgs.inputPaths.empty())
return formatError("the compilation database path argument is missing");
if(toolArgs.inputPaths.size() > 1)
return formatError("got {} input paths where 1 was expected", toolArgs.inputPaths.size());
MRDOCS_CHECK(toolArgs.inputPaths, "The compilation database path argument is missing");
MRDOCS_CHECK(toolArgs.inputPaths.size() == 1,
formatError(
"got {} input paths where 1 was expected",
toolArgs.inputPaths.size()));
auto compilationsPath = files::normalizePath(toolArgs.inputPaths.front());
std::string errorMessage;
auto jsonCompilations = tooling::JSONCompilationDatabase::loadFromFile(
compilationsPath, errorMessage, tooling::JSONCommandLineSyntax::AutoDetect);
if(! jsonCompilations)
return Error(std::move(errorMessage));
MRDOCS_TRY_MSG(
auto& jsonCompilations,
tooling::JSONCompilationDatabase::loadFromFile(
compilationsPath,
errorMessage,
tooling::JSONCommandLineSyntax::AutoDetect),
std::move(errorMessage));

// Calculate the working directory
auto absPath = files::makeAbsolute(compilationsPath);
if(! absPath)
return absPath.error();
auto workingDir = files::getParentDir(*absPath);
MRDOCS_TRY(auto absPath, files::makeAbsolute(compilationsPath));
auto workingDir = files::getParentDir(absPath);

// normalize outputPath
if( toolArgs.outputPath.empty())
return formatError("output path is empty");
// Normalize outputPath
MRDOCS_CHECK(toolArgs.outputPath, "The output path argument is missing");
toolArgs.outputPath = files::normalizePath(
files::makeAbsolute(toolArgs.outputPath,
(*config)->workingDir));

// Convert relative paths to absolute
AbsoluteCompilationDatabase compilations(
workingDir, *jsonCompilations, config);
workingDir, jsonCompilations, config);

// Run the tool, this can take a while
auto corpus = CorpusImpl::build(
report::Level::info, config, compilations);
if(! corpus)
return formatError("CorpusImpl::build returned \"{}\"", corpus.error());
// Run the tool: this can take a while
MRDOCS_TRY(
auto corpus,
CorpusImpl::build(
report::Level::info, config, compilations));

// Run the generator.
// Run the generator
report::info("Generating docs\n");
return generator->build(toolArgs.outputPath.getValue(), **corpus);
MRDOCS_TRY(generator.build(toolArgs.outputPath.getValue(), *corpus));
return {};
}

} // mrdocs
Expand Down
Loading

0 comments on commit 3e1c279

Please sign in to comment.