Improve error messages for exceptions in AExpressions.

Refs #5846
This commit is contained in:
Gunnar Beutner 2014-03-22 08:40:09 +01:00
parent 3c067e9547
commit 2a6a75d52e
4 changed files with 113 additions and 94 deletions

View File

@ -18,11 +18,14 @@
******************************************************************************/ ******************************************************************************/
#include "config/aexpression.h" #include "config/aexpression.h"
#include "config/configerror.h"
#include "base/array.h" #include "base/array.h"
#include "base/serializer.h" #include "base/serializer.h"
#include "base/context.h" #include "base/context.h"
#include "base/scriptfunction.h" #include "base/scriptfunction.h"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/exception_ptr.hpp>
#include <boost/exception/errinfo_nested_exception.hpp>
using namespace icinga; 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); msgbuf << "Evaluating AExpression " << m_DebugInfo << "; left=" << JsonSerialize(left) << "; right=" << JsonSerialize(right);
CONTEXT(msgbuf.str()); CONTEXT(msgbuf.str());
switch (m_Operator) { try {
case AEReturn: switch (m_Operator) {
return left; case AEReturn:
case AENegate: return left;
return ~(long)left; case AENegate:
case AEAdd: return ~(long)left;
return left + right; case AEAdd:
case AESubtract: return left + right;
return left - right; case AESubtract:
case AEMultiply: return left - right;
return left * right; case AEMultiply:
case AEDivide: return left * right;
return left / right; case AEDivide:
case AEBinaryAnd: return left / right;
return left & right; case AEBinaryAnd:
case AEBinaryOr: return left & right;
return left | right; case AEBinaryOr:
case AEShiftLeft: return left | right;
return left << right; case AEShiftLeft:
case AEShiftRight: return left << right;
return left >> right; case AEShiftRight:
case AEEqual: return left >> right;
return left == right; case AEEqual:
case AENotEqual: return left == right;
return left != right; case AENotEqual:
case AELessThan: return left != right;
return left < right; case AELessThan:
case AEGreaterThan: return left < right;
return left > right; case AEGreaterThan:
case AELessThanOrEqual: return left > right;
return left <= right; case AELessThanOrEqual:
case AEGreaterThanOrEqual: return left <= right;
return left >= right; case AEGreaterThanOrEqual:
case AEIn: return left >= right;
if (!right.IsObjectType<Array>()) case AEIn:
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid right side argument for 'in' operator: " + JsonSerialize(right))); if (!right.IsObjectType<Array>())
BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonSerialize(right)));
arr = right; arr = right;
found = false; found = false;
BOOST_FOREACH(const Value& value, arr) { BOOST_FOREACH(const Value& value, arr) {
if (value == left) { if (value == left) {
found = true; found = true;
break; break;
}
} }
}
return found; return found;
case AENotIn: case AENotIn:
if (!right.IsObjectType<Array>()) if (!right.IsObjectType<Array>())
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid right side argument for 'in' operator: " + JsonSerialize(right))); BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonSerialize(right)));
arr = right; arr = right;
found = false; found = false;
BOOST_FOREACH(const Value& value, arr) { BOOST_FOREACH(const Value& value, arr) {
if (value == left) { if (value == left) {
found = true; found = true;
break; break;
}
} }
}
return !found; return !found;
case AELogicalAnd: case AELogicalAnd:
return left.ToBool() && right.ToBool(); return left.ToBool() && right.ToBool();
case AELogicalOr: case AELogicalOr:
return left.ToBool() || right.ToBool(); return left.ToBool() || right.ToBool();
case AEFunctionCall: case AEFunctionCall:
funcName = left; funcName = left;
func = ScriptFunctionRegistry::GetInstance()->GetItem(funcName); func = ScriptFunctionRegistry::GetInstance()->GetItem(funcName);
if (!func) if (!func)
BOOST_THROW_EXCEPTION(std::invalid_argument("Function '" + funcName + "' does not exist.")); BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist."));
arr = right; 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<Array>();
if (arr) {
BOOST_FOREACH(const AExpression::Ptr& aexpr, arr) { BOOST_FOREACH(const AExpression::Ptr& aexpr, arr) {
arr2->Add(aexpr->Evaluate(locals)); arguments.push_back(aexpr->Evaluate(locals));
} }
}
return arr2; return func->Invoke(arguments);
default: case AEArray:
ASSERT(!"Invalid operator."); arr = left;
arr2 = make_shared<Array>();
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<boost::errinfo_nested_exception>(ex))
throw;
else
BOOST_THROW_EXCEPTION(ConfigError("Error while evaluating expression.") << boost::errinfo_nested_exception(boost::current_exception()) << errinfo_debuginfo(m_DebugInfo));
} }
} }

View File

@ -190,7 +190,8 @@ void ConfigCompiler::Compile(void)
try { try {
yyparse(this); yyparse(this);
} catch (const ConfigError& ex) { } catch (const ConfigError& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), ex.GetDebugInfo()); const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex)); ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
} }
@ -285,7 +286,7 @@ type: partial_specifier T_TYPE identifier
if (!m_Type) { if (!m_Type) {
if ($1) 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<ConfigType>(name, DebugInfoRange(@1, @3)); m_Type = make_shared<ConfigType>(name, DebugInfoRange(@1, @3));
m_Type->Register(); m_Type->Register();
@ -406,7 +407,7 @@ object:
free($3); free($3);
free($4); free($4);
delete $5; delete $5;
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str(), di)); BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(di));
} }
item->SetType($3); item->SetType($3);
@ -415,7 +416,7 @@ object:
std::ostringstream msgbuf; std::ostringstream msgbuf;
msgbuf << "Name for object '" << $4 << "' of type '" << $3 << "' is invalid: Object names may not contain '!'"; msgbuf << "Name for object '" << $4 << "' of type '" << $3 << "' is invalid: Object names may not contain '!'";
free($3); free($3);
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str(), @4)); BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(@4));
} }
free($3); free($3);
@ -754,7 +755,7 @@ optional_template: /* empty */
apply: T_APPLY optional_template identifier identifier T_TO identifier T_WHERE aexpression apply: T_APPLY optional_template identifier identifier T_TO identifier T_WHERE aexpression
{ {
if (!ApplyRule::IsValidCombination($3, $6)) { 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<Array>(); Array::Ptr arguments = make_shared<Array>();

View File

@ -18,11 +18,12 @@
******************************************************************************/ ******************************************************************************/
#include "config/configerror.h" #include "config/configerror.h"
#include <sstream>
using namespace icinga; using namespace icinga;
ConfigError::ConfigError(const String& message, const DebugInfo& di) ConfigError::ConfigError(const String& message)
: m_Message(message), m_DebugInfo(di) : m_Message(message)
{ } { }
ConfigError::~ConfigError(void) throw() ConfigError::~ConfigError(void) throw()
@ -33,7 +34,10 @@ const char *ConfigError::what(void) const throw()
return m_Message.CStr(); return m_Message.CStr();
} }
DebugInfo ConfigError::GetDebugInfo(void) const std::string icinga::to_string(const errinfo_debuginfo& e)
{ {
return m_DebugInfo; std::ostringstream msgbuf;
msgbuf << "Config location: " << e.value() << "\n";
ShowCodeFragment(msgbuf, e.value());
return msgbuf.str();
} }

View File

@ -22,6 +22,7 @@
#include "config/i2-config.h" #include "config/i2-config.h"
#include "config/debuginfo.h" #include "config/debuginfo.h"
#include "base/exception.h"
namespace icinga namespace icinga
{ {
@ -29,20 +30,23 @@ namespace icinga
/* /*
* @ingroup config * @ingroup config
*/ */
class I2_CONFIG_API ConfigError : public std::exception class I2_CONFIG_API ConfigError : virtual public user_error
{ {
public: public:
ConfigError(const String& message, const DebugInfo& di); ConfigError(const String& message);
~ConfigError(void) throw(); ~ConfigError(void) throw();
const char *what(void) const throw(); const char *what(void) const throw();
DebugInfo GetDebugInfo(void) const;
private: private:
String m_Message; String m_Message;
DebugInfo m_DebugInfo;
}; };
struct errinfo_debuginfo_;
typedef boost::error_info<struct errinfo_debuginfo_, DebugInfo> errinfo_debuginfo;
std::string to_string(const errinfo_debuginfo& e);
} }
#endif /* CONFIGERROR_H */ #endif /* CONFIGERROR_H */