diff options
author | Klaus Aehlig <klaus.aehlig@huawei.com> | 2024-04-04 10:13:17 +0200 |
---|---|---|
committer | Klaus Aehlig <klaus.aehlig@huawei.com> | 2024-04-05 14:48:26 +0200 |
commit | 427b6e4e83486858b1ab160c75e2754ba173eebe (patch) | |
tree | a8f46108ab2796ea56eac847f0f5bc8d9e3fbefe /src | |
parent | e704f5a976809eaf76f2d1b29616c24a15a113ed (diff) | |
download | justbuild-427b6e4e83486858b1ab160c75e2754ba173eebe.tar.gz |
Evaluator: Add infrastructure to annotate relevant objects
... which are, in particular, artifacts involved in staging conflicts.
While there, also make disjoint union honor the expression log limit.
Diffstat (limited to 'src')
5 files changed, 114 insertions, 23 deletions
diff --git a/src/buildtool/build_engine/base_maps/expression_function.hpp b/src/buildtool/build_engine/base_maps/expression_function.hpp index 41e6ad83..a5235807 100644 --- a/src/buildtool/build_engine/base_maps/expression_function.hpp +++ b/src/buildtool/build_engine/base_maps/expression_function.hpp @@ -51,11 +51,13 @@ class ExpressionFunction { [](std::string const& error) noexcept -> void { Logger::Log(LogLevel::Error, error); }, + std::function<std::string(ExpressionPtr)> annotate_object = + [](auto const& /*unused*/) { return std::string{}; }, std::function<void(void)> const& note_user_context = []() noexcept -> void {}) const noexcept -> ExpressionPtr { try { // try-catch to silence clang-tidy's bugprone-exception-escape, // only imports_caller can throw but it is not called here. - auto imports_caller = [this, &functions]( + auto imports_caller = [this, &functions, &annotate_object]( SubExprEvaluator&& /*eval*/, ExpressionPtr const& expr, Configuration const& env) { @@ -69,6 +71,7 @@ class ExpressionFunction { env, functions, [&ss](auto const& msg) { ss << msg; }, + annotate_object, [&user_context]() { user_context = true; } ); @@ -94,6 +97,7 @@ class ExpressionFunction { FunctionMap::MakePtr( functions, "CALL_EXPRESSION", imports_caller), logger, + annotate_object, note_user_context); } catch (...) { EnsuresAudit(false); // ensure that the try-block never throws diff --git a/src/buildtool/build_engine/expression/evaluator.cpp b/src/buildtool/build_engine/expression/evaluator.cpp index 34e0873b..b642eeec 100644 --- a/src/buildtool/build_engine/expression/evaluator.cpp +++ b/src/buildtool/build_engine/expression/evaluator.cpp @@ -361,8 +361,13 @@ auto Union(Expression::list_t const& dicts, size_t from, size_t to) fmt::format("Map union not essentially disjoint as claimed, " "duplicate key {}; conflicting values:\n- {}\n- {}", nlohmann::json(dup->get()).dump(), - left_val->ToString(), - right_val->ToString())}; + left_val->ToAbbrevString( + Evaluator::GetExpressionLogLimit()), + right_val->ToAbbrevString( + Evaluator::GetExpressionLogLimit())), + false, + false, + std::vector<ExpressionPtr>{left_val, right_val}}; } } return ExpressionPtr{Expression::map_t{left, right}}; @@ -432,12 +437,14 @@ auto UnaryExpr(std::function<ExpressionPtr(ExpressionPtr const&)> const& f) } catch (Evaluator::EvaluationError const& ex) { throw Evaluator::EvaluationError::WhileEval( fmt::format("Having evaluated the argument to {}:", - argument->ToString()), + argument->ToAbbrevString( + Evaluator::GetExpressionLogLimit())), ex); } catch (std::exception const& ex) { throw Evaluator::EvaluationError::WhileEvaluating( fmt::format("Having evaluated the argument to {}:", - argument->ToString()), + argument->ToAbbrevString( + Evaluator::GetExpressionLogLimit())), ex); } }; @@ -897,6 +904,20 @@ auto ContextExpr(SubExprEvaluator&& eval, Configuration const& env) -> ExpressionPtr { try { return eval(expr->Get("$1", Expression::kNone), env); + } catch (Evaluator::EvaluationError const& ex) { + auto msg_expr = expr->Get("msg", map_t{}); + std::string context{}; + try { + auto msg_val = eval(msg_expr, env); + context = msg_val->ToString(); + } catch (std::exception const&) { + context = "[non evaluating term] " + msg_expr->ToString(); + } + std::stringstream ss{}; + ss << "In Context " << context << std::endl; + ss << ex.what(); + throw Evaluator::EvaluationError( + ss.str(), true, true, ex.InvolvedObjects()); } catch (std::exception const& ex) { auto msg_expr = expr->Get("msg", map_t{}); std::string context{}; @@ -924,12 +945,36 @@ auto DisjointUnionExpr(SubExprEvaluator&& eval, } try { return Union</*kDisjoint=*/true>(argument); + } catch (Evaluator::EvaluationError const& ex) { + auto msg_expr = expr->Map().Find("msg"); + if (not msg_expr) { + throw Evaluator::EvaluationError::WhileEval( + fmt::format("Having evaluated the argument to {}:", + argument->ToAbbrevString( + Evaluator::GetExpressionLogLimit())), + ex); + } + std::string msg; + try { + auto msg_val = eval(**msg_expr, env); + msg = msg_val->ToString(); + } catch (std::exception const&) { + msg = "[non evaluating term] " + (**msg_expr)->ToString(); + } + std::stringstream ss{}; + ss << msg << std::endl; + ss << "Underlying " << ex.what() << std::endl; + ss << "The argument of the union was " + << argument->ToAbbrevString(Evaluator::GetExpressionLogLimit()); + throw Evaluator::EvaluationError( + ss.str(), false, true, ex.InvolvedObjects()); } catch (std::exception const& ex) { auto msg_expr = expr->Map().Find("msg"); if (not msg_expr) { throw Evaluator::EvaluationError::WhileEvaluating( fmt::format("Having evaluated the argument to {}:", - argument->ToString()), + argument->ToAbbrevString( + Evaluator::GetExpressionLogLimit())), ex); } std::string msg; @@ -942,7 +987,8 @@ auto DisjointUnionExpr(SubExprEvaluator&& eval, std::stringstream ss{}; ss << msg << std::endl; ss << "Reason: " << ex.what() << std::endl; - ss << "The argument of the union was " << argument->ToString(); + ss << "The argument of the union was " + << argument->ToAbbrevString(Evaluator::GetExpressionLogLimit()); throw Evaluator::EvaluationError(ss.str(), false, true); } } @@ -1021,23 +1067,31 @@ auto const kBuiltInFunctions = {"env", EnvExpr}, {"concat_target_name", ConcatTargetNameExpr}}); -} // namespace - -auto Evaluator::EvaluationError::WhileEvaluating(ExpressionPtr const& expr, - Configuration const& env, - std::exception const& ex) - -> Evaluator::EvaluationError { +auto ExtendedErrorMessage(ExpressionPtr const& expr, + Configuration const& env, + std::exception const& ex) -> std::string { std::stringstream ss{}; ss << "* "; if (expr->IsMap() and expr->Map().contains("type") and expr["type"]->IsString()) { ss << expr["type"]->ToString() << "-expression "; } - ss << expr->ToAbbrevString(Config().expression_log_limit) << std::endl; + ss << expr->ToAbbrevString(Evaluator::GetExpressionLogLimit()) << std::endl; ss << " environment " << std::endl; - ss << env.Enumerate(" - ", Config().expression_log_limit) << std::endl; + ss << env.Enumerate(" - ", Evaluator::GetExpressionLogLimit()) + << std::endl; ss << ex.what(); - return EvaluationError{ss.str(), true /* while_eval */}; + return ss.str(); +} + +} // namespace + +auto Evaluator::EvaluationError::WhileEvaluating(ExpressionPtr const& expr, + Configuration const& env, + std::exception const& ex) + -> Evaluator::EvaluationError { + return EvaluationError{ExtendedErrorMessage(expr, env, ex), + true /* while_eval */}; } auto Evaluator::EvaluationError::WhileEval(ExpressionPtr const& expr, @@ -1047,7 +1101,8 @@ auto Evaluator::EvaluationError::WhileEval(ExpressionPtr const& expr, if (ex.UserContext()) { return ex; } - return Evaluator::EvaluationError::WhileEvaluating(expr, env, ex); + return EvaluationError{ + ExtendedErrorMessage(expr, env, ex), true, false, ex.InvolvedObjects()}; } auto Evaluator::EvaluationError::WhileEvaluating(const std::string& where, @@ -1065,7 +1120,10 @@ auto Evaluator::EvaluationError::WhileEval(const std::string& where, if (ex.UserContext()) { return ex; } - return Evaluator::EvaluationError::WhileEvaluating(where, ex); + std::stringstream ss{}; + ss << where << std::endl; + ss << ex.what(); + return EvaluationError{ss.str(), true, false, ex.InvolvedObjects()}; } auto Evaluator::EvaluateExpression( @@ -1073,6 +1131,7 @@ auto Evaluator::EvaluateExpression( Configuration const& env, FunctionMapPtr const& provider_functions, std::function<void(std::string const&)> const& logger, + std::function<std::string(ExpressionPtr)> const& annotate_object, std::function<void(void)> const& note_user_context) noexcept -> ExpressionPtr { std::stringstream ss{}; @@ -1096,6 +1155,9 @@ auto Evaluator::EvaluateExpression( } } ss << ex.what(); + for (auto const& object : ex.InvolvedObjects()) { + ss << annotate_object(object); + } } catch (std::exception const& ex) { ss << ex.what(); } diff --git a/src/buildtool/build_engine/expression/evaluator.hpp b/src/buildtool/build_engine/expression/evaluator.hpp index efcbeaca..598b36d4 100644 --- a/src/buildtool/build_engine/expression/evaluator.hpp +++ b/src/buildtool/build_engine/expression/evaluator.hpp @@ -18,6 +18,8 @@ #include <cstddef> #include <exception> #include <string> +#include <utility> +#include <vector> #include "src/buildtool/build_engine/expression/expression.hpp" #include "src/buildtool/build_engine/expression/function_map.hpp" @@ -36,17 +38,24 @@ class Evaluator { Config().expression_log_limit = width; } + static auto GetExpressionLogLimit() -> std::size_t { + return Config().expression_log_limit; + } + class EvaluationError : public std::exception { public: explicit EvaluationError(std::string const& msg, bool while_eval = false, - bool user_context = false) noexcept + bool user_context = false, + std::vector<ExpressionPtr> involved_objetcs = + std::vector<ExpressionPtr>{}) noexcept : msg_{(while_eval ? "" : (user_context ? "UserError: " : "EvaluationError: ")) + msg}, while_eval_{while_eval}, - user_context_{user_context} {} + user_context_{user_context}, + involved_objects_{std::move(std::move(involved_objetcs))} {} [[nodiscard]] auto what() const noexcept -> char const* final { return msg_.c_str(); } @@ -57,6 +66,15 @@ class Evaluator { [[nodiscard]] auto UserContext() const -> bool { return user_context_; } + [[nodiscard]] auto InvolvedObjects() const& noexcept + -> std::vector<ExpressionPtr> const& { + return involved_objects_; + } + + [[nodiscard]] auto InvolvedObjects() && -> std::vector<ExpressionPtr> { + return involved_objects_; + } + [[nodiscard]] static auto WhileEvaluating(ExpressionPtr const& expr, Configuration const& env, std::exception const& ex) @@ -79,6 +97,7 @@ class Evaluator { std::string msg_; bool while_eval_; bool user_context_; + std::vector<ExpressionPtr> involved_objects_; }; // Exception-free evaluation of expression @@ -87,6 +106,8 @@ class Evaluator { Configuration const& env, FunctionMapPtr const& provider_functions, std::function<void(std::string const&)> const& logger, + std::function<std::string(ExpressionPtr)> const& annotate_object = + [](auto const& /*unused*/) { return std::string{}; }, std::function<void(void)> const& note_user_context = []() {}) noexcept -> ExpressionPtr; diff --git a/src/buildtool/build_engine/expression/expression_ptr.cpp b/src/buildtool/build_engine/expression/expression_ptr.cpp index 368d2816..68b45c6f 100644 --- a/src/buildtool/build_engine/expression/expression_ptr.cpp +++ b/src/buildtool/build_engine/expression/expression_ptr.cpp @@ -60,10 +60,12 @@ auto ExpressionPtr::Evaluate( Configuration const& env, FunctionMapPtr const& functions, std::function<void(std::string const&)> const& logger, - std::function<void(void)> const& note_user_context) const noexcept - -> ExpressionPtr { + std::function<std::string(ExpressionPtr)> const& annotate_object, + std::function<void(void)> const& note_user_context + +) const noexcept -> ExpressionPtr { return Evaluator::EvaluateExpression( - *this, env, functions, logger, note_user_context); + *this, env, functions, logger, annotate_object, note_user_context); } auto ExpressionPtr::IsCacheable() const noexcept -> bool { diff --git a/src/buildtool/build_engine/expression/expression_ptr.hpp b/src/buildtool/build_engine/expression/expression_ptr.hpp index 77b1cfba..f8b9a77f 100644 --- a/src/buildtool/build_engine/expression/expression_ptr.hpp +++ b/src/buildtool/build_engine/expression/expression_ptr.hpp @@ -83,6 +83,8 @@ class ExpressionPtr { [](std::string const& error) noexcept -> void { Logger::Log(LogLevel::Error, error); }, + std::function<std::string(ExpressionPtr)> const& annotate_object = + [](auto const& /*unused*/) { return std::string{}; }, std::function<void(void)> const& note_user_context = []() noexcept -> void {}) const noexcept -> ExpressionPtr; |