From 2a6a75d52e91711245e72ff4308aeb380a04fea4 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Sat, 22 Mar 2014 08:40:09 +0100 Subject: [PATCH] Improve error messages for exceptions in AExpressions. Refs #5846 --- lib/config/aexpression.cpp | 170 +++++++++++++++++++----------------- lib/config/config_parser.yy | 11 +-- lib/config/configerror.cpp | 14 +-- lib/config/configerror.h | 12 ++- 4 files changed, 113 insertions(+), 94 deletions(-) diff --git a/lib/config/aexpression.cpp b/lib/config/aexpression.cpp index e96cd8c53..d3c4983ec 100644 --- a/lib/config/aexpression.cpp +++ b/lib/config/aexpression.cpp @@ -18,11 +18,14 @@ ******************************************************************************/ #include "config/aexpression.h" +#include "config/configerror.h" #include "base/array.h" #include "base/serializer.h" #include "base/context.h" #include "base/scriptfunction.h" #include +#include +#include using namespace icinga; @@ -57,96 +60,103 @@ Value AExpression::Evaluate(const Dictionary::Ptr& locals) const msgbuf << "Evaluating AExpression " << m_DebugInfo << "; left=" << JsonSerialize(left) << "; right=" << JsonSerialize(right); CONTEXT(msgbuf.str()); - switch (m_Operator) { - case AEReturn: - return left; - case AENegate: - return ~(long)left; - case AEAdd: - return left + right; - case AESubtract: - return left - right; - case AEMultiply: - return left * right; - case AEDivide: - return left / right; - case AEBinaryAnd: - return left & right; - case AEBinaryOr: - return left | right; - case AEShiftLeft: - return left << right; - case AEShiftRight: - return left >> right; - case AEEqual: - return left == right; - case AENotEqual: - return left != right; - case AELessThan: - return left < right; - case AEGreaterThan: - return left > right; - case AELessThanOrEqual: - return left <= right; - case AEGreaterThanOrEqual: - return left >= right; - case AEIn: - if (!right.IsObjectType()) - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid right side argument for 'in' operator: " + JsonSerialize(right))); + try { + switch (m_Operator) { + case AEReturn: + return left; + case AENegate: + return ~(long)left; + case AEAdd: + return left + right; + case AESubtract: + return left - right; + case AEMultiply: + return left * right; + case AEDivide: + return left / right; + case AEBinaryAnd: + return left & right; + case AEBinaryOr: + return left | right; + case AEShiftLeft: + return left << right; + case AEShiftRight: + return left >> right; + case AEEqual: + return left == right; + case AENotEqual: + return left != right; + case AELessThan: + return left < right; + case AEGreaterThan: + return left > right; + case AELessThanOrEqual: + return left <= right; + case AEGreaterThanOrEqual: + return left >= right; + case AEIn: + if (!right.IsObjectType()) + BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonSerialize(right))); - arr = right; - found = false; - BOOST_FOREACH(const Value& value, arr) { - if (value == left) { - found = true; - break; + arr = right; + found = false; + BOOST_FOREACH(const Value& value, arr) { + if (value == left) { + found = true; + break; + } } - } - return found; - case AENotIn: - if (!right.IsObjectType()) - BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid right side argument for 'in' operator: " + JsonSerialize(right))); + return found; + case AENotIn: + if (!right.IsObjectType()) + BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonSerialize(right))); - arr = right; - found = false; - BOOST_FOREACH(const Value& value, arr) { - if (value == left) { - found = true; - break; + arr = right; + found = false; + BOOST_FOREACH(const Value& value, arr) { + if (value == left) { + found = true; + break; + } } - } - return !found; - case AELogicalAnd: - return left.ToBool() && right.ToBool(); - case AELogicalOr: - return left.ToBool() || right.ToBool(); - case AEFunctionCall: - funcName = left; - func = ScriptFunctionRegistry::GetInstance()->GetItem(funcName); + return !found; + case AELogicalAnd: + return left.ToBool() && right.ToBool(); + case AELogicalOr: + return left.ToBool() || right.ToBool(); + case AEFunctionCall: + funcName = left; + func = ScriptFunctionRegistry::GetInstance()->GetItem(funcName); - if (!func) - BOOST_THROW_EXCEPTION(std::invalid_argument("Function '" + funcName + "' does not exist.")); + if (!func) + BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist.")); - arr = right; - BOOST_FOREACH(const AExpression::Ptr& aexpr, arr) { - arguments.push_back(aexpr->Evaluate(locals)); - } - - return func->Invoke(arguments); - case AEArray: - arr = left; - arr2 = make_shared(); - - if (arr) { + arr = right; BOOST_FOREACH(const AExpression::Ptr& aexpr, arr) { - arr2->Add(aexpr->Evaluate(locals)); + arguments.push_back(aexpr->Evaluate(locals)); } - } - return arr2; - default: - ASSERT(!"Invalid operator."); + return func->Invoke(arguments); + case AEArray: + arr = left; + arr2 = make_shared(); + + if (arr) { + BOOST_FOREACH(const AExpression::Ptr& aexpr, arr) { + arr2->Add(aexpr->Evaluate(locals)); + } + } + + return arr2; + default: + ASSERT(!"Invalid operator."); + } + } catch (const std::exception& ex) { + if (boost::get_error_info(ex)) + throw; + else + BOOST_THROW_EXCEPTION(ConfigError("Error while evaluating expression.") << boost::errinfo_nested_exception(boost::current_exception()) << errinfo_debuginfo(m_DebugInfo)); } } diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 976f0d4b7..c58f3f71e 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -190,7 +190,8 @@ void ConfigCompiler::Compile(void) try { yyparse(this); } catch (const ConfigError& ex) { - ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo()); + const DebugInfo *di = boost::get_error_info(ex); + ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo()); } catch (const std::exception& ex) { ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex)); } @@ -285,7 +286,7 @@ type: partial_specifier T_TYPE identifier if (!m_Type) { if ($1) - BOOST_THROW_EXCEPTION(ConfigError("Partial type definition for unknown type '" + name + "'", DebugInfoRange(@1, @3))); + BOOST_THROW_EXCEPTION(ConfigError("Partial type definition for unknown type '" + name + "'") << errinfo_debuginfo(DebugInfoRange(@1, @3))); m_Type = make_shared(name, DebugInfoRange(@1, @3)); m_Type->Register(); @@ -406,7 +407,7 @@ object: free($3); free($4); delete $5; - BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str(), di)); + BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(di)); } item->SetType($3); @@ -415,7 +416,7 @@ object: std::ostringstream msgbuf; msgbuf << "Name for object '" << $4 << "' of type '" << $3 << "' is invalid: Object names may not contain '!'"; free($3); - BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str(), @4)); + BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(@4)); } free($3); @@ -754,7 +755,7 @@ optional_template: /* empty */ apply: T_APPLY optional_template identifier identifier T_TO identifier T_WHERE aexpression { if (!ApplyRule::IsValidCombination($3, $6)) { - BOOST_THROW_EXCEPTION(ConfigError("'apply' cannot be used with types '" + String($3) + "' and '" + String($6) + "'.", @1)); + BOOST_THROW_EXCEPTION(ConfigError("'apply' cannot be used with types '" + String($3) + "' and '" + String($6) + "'.") << errinfo_debuginfo(@1)); } Array::Ptr arguments = make_shared(); diff --git a/lib/config/configerror.cpp b/lib/config/configerror.cpp index a4b232c35..1f3c092f7 100644 --- a/lib/config/configerror.cpp +++ b/lib/config/configerror.cpp @@ -18,11 +18,12 @@ ******************************************************************************/ #include "config/configerror.h" +#include using namespace icinga; -ConfigError::ConfigError(const String& message, const DebugInfo& di) - : m_Message(message), m_DebugInfo(di) +ConfigError::ConfigError(const String& message) + : m_Message(message) { } ConfigError::~ConfigError(void) throw() @@ -33,7 +34,10 @@ const char *ConfigError::what(void) const throw() return m_Message.CStr(); } -DebugInfo ConfigError::GetDebugInfo(void) const +std::string icinga::to_string(const errinfo_debuginfo& e) { - return m_DebugInfo; -} \ No newline at end of file + std::ostringstream msgbuf; + msgbuf << "Config location: " << e.value() << "\n"; + ShowCodeFragment(msgbuf, e.value()); + return msgbuf.str(); +} diff --git a/lib/config/configerror.h b/lib/config/configerror.h index 10bccfe4e..c60725b99 100644 --- a/lib/config/configerror.h +++ b/lib/config/configerror.h @@ -22,6 +22,7 @@ #include "config/i2-config.h" #include "config/debuginfo.h" +#include "base/exception.h" namespace icinga { @@ -29,20 +30,23 @@ namespace icinga /* * @ingroup config */ -class I2_CONFIG_API ConfigError : public std::exception +class I2_CONFIG_API ConfigError : virtual public user_error { public: - ConfigError(const String& message, const DebugInfo& di); + ConfigError(const String& message); ~ConfigError(void) throw(); const char *what(void) const throw(); - DebugInfo GetDebugInfo(void) const; private: String m_Message; - DebugInfo m_DebugInfo; }; +struct errinfo_debuginfo_; +typedef boost::error_info errinfo_debuginfo; + +std::string to_string(const errinfo_debuginfo& e); + } #endif /* CONFIGERROR_H */