diff --git a/include/jinja2cpp/error_info.h b/include/jinja2cpp/error_info.h index 16bfcc2f..98a84633 100644 --- a/include/jinja2cpp/error_info.h +++ b/include/jinja2cpp/error_info.h @@ -34,7 +34,9 @@ enum class ErrorCode TemplateNotFound, TemplateNotParsed, InvalidValueType, + InvalidTemplateName, ExtensionDisabled, + TemplateEnvAbsent, }; struct SourceLocation diff --git a/include/jinja2cpp/filesystem_handler.h b/include/jinja2cpp/filesystem_handler.h index 5dbc7a59..19fbb05f 100644 --- a/include/jinja2cpp/filesystem_handler.h +++ b/include/jinja2cpp/filesystem_handler.h @@ -48,7 +48,7 @@ class RealFileSystem : public IFilesystemHandler void SetRootFolder(std::string newRoot) { - m_rootFolder = newRoot; + m_rootFolder = std::move(newRoot); } CharFileStreamPtr OpenStream(const std::string& name) const override; diff --git a/src/error_info.cpp b/src/error_info.cpp index bc82c438..b19bace0 100644 --- a/src/error_info.cpp +++ b/src/error_info.cpp @@ -110,8 +110,11 @@ void RenderErrorInfo(std::basic_ostream& os, const ErrorInfoTpl& e os << UNIVERSAL_STR("Parse error"); break; case ErrorCode::UnexpectedException: - os << UNIVERSAL_STR("Unexpected exception occurred during template processing"); + { + auto& extraParams = errInfo.GetExtraParams(); + os << UNIVERSAL_STR("Unexpected exception occurred during template processing. Exception: ") << extraParams[0]; break; + } case ErrorCode::YetUnsupported: os << UNIVERSAL_STR("This feature has not been supported yet"); break; @@ -197,12 +200,18 @@ void RenderErrorInfo(std::basic_ostream& os, const ErrorInfoTpl& e case ErrorCode::TemplateNotFound: os << UNIVERSAL_STR("Template(s) not found: ") << errInfo.GetExtraParams()[0]; break; + case ErrorCode::InvalidTemplateName: + os << UNIVERSAL_STR("Invalid template name: ") << errInfo.GetExtraParams()[0]; + break; case ErrorCode::InvalidValueType: os << UNIVERSAL_STR("Invalid value type"); break; case ErrorCode::ExtensionDisabled: os << UNIVERSAL_STR("Extension disabled"); break; + case ErrorCode::TemplateEnvAbsent: + os << UNIVERSAL_STR("Template environment doesn't set"); + break; } os << std::endl << errInfo.GetLocationDescr(); } diff --git a/src/expression_evaluator.cpp b/src/expression_evaluator.cpp index b8b51fe7..9486985d 100644 --- a/src/expression_evaluator.cpp +++ b/src/expression_evaluator.cpp @@ -181,9 +181,9 @@ InternalValue DictCreator::Evaluate(RenderContext& context) return MapAdapter::CreateAdapter(std::move(result));; } -ExpressionFilter::ExpressionFilter(std::string filterName, CallParams params) +ExpressionFilter::ExpressionFilter(const std::string& filterName, CallParams params) { - m_filter = CreateFilter(std::move(filterName), std::move(params)); + m_filter = CreateFilter(filterName, std::move(params)); if (!m_filter) throw std::runtime_error("Can't find filter '" + filterName + "'"); } @@ -196,10 +196,10 @@ InternalValue ExpressionFilter::Evaluate(const InternalValue& baseVal, RenderCon return m_filter->Filter(baseVal, context); } -IsExpression::IsExpression(ExpressionEvaluatorPtr<> value, std::string tester, CallParams params) +IsExpression::IsExpression(ExpressionEvaluatorPtr<> value, const std::string& tester, CallParams params) : m_value(value) { - m_tester = CreateTester(std::move(tester), std::move(params)); + m_tester = CreateTester(tester, std::move(params)); if (!m_tester) throw std::runtime_error("Can't find tester '" + tester + "'"); } diff --git a/src/expression_evaluator.h b/src/expression_evaluator.h index db644958..17626008 100644 --- a/src/expression_evaluator.h +++ b/src/expression_evaluator.h @@ -49,7 +49,7 @@ struct ParsedArguments std::unordered_map> extraKwArgs; std::vector> extraPosArgs; - ExpressionEvaluatorPtr<> operator[](std::string name) const + ExpressionEvaluatorPtr<> operator[](const std::string& name) const { auto p = args.find(name); if (p == args.end()) @@ -67,15 +67,15 @@ class FullExpressionEvaluator : public ExpressionEvaluatorBase public: void SetExpression(ExpressionEvaluatorPtr expr) { - m_expression = expr; + m_expression = std::move(expr); } void SetFilter(ExpressionEvaluatorPtr expr) { - m_filter = expr; + m_filter = std::move(expr); } void SetTester(ExpressionEvaluatorPtr expr) { - m_tester = expr; + m_tester = std::move(expr); } InternalValue Evaluate(RenderContext& values) override; void Render(OutStream &stream, RenderContext &values) override; @@ -204,7 +204,7 @@ class IsExpression : public Expression using TesterFactoryFn = std::function (CallParams params)>; - IsExpression(ExpressionEvaluatorPtr<> value, std::string tester, CallParams params); + IsExpression(ExpressionEvaluatorPtr<> value, const std::string& tester, CallParams params); InternalValue Evaluate(RenderContext& context) override; private: @@ -293,12 +293,12 @@ class ExpressionFilter using FilterFactoryFn = std::function (CallParams params)>; - ExpressionFilter(std::string filterName, CallParams params); + ExpressionFilter(const std::string& filterName, CallParams params); InternalValue Evaluate(const InternalValue& baseVal, RenderContext& context); void SetParentFilter(std::shared_ptr parentFilter) { - m_parentFilter = parentFilter; + m_parentFilter = std::move(parentFilter); } private: std::shared_ptr m_filter; @@ -322,7 +322,7 @@ class IfExpression void SetAltValue(ExpressionEvaluatorPtr<> altValue) { - m_altValue = altValue; + m_altValue = std::move(altValue); } private: diff --git a/src/expression_parser.cpp b/src/expression_parser.cpp index 1c198541..7131b300 100644 --- a/src/expression_parser.cpp +++ b/src/expression_parser.cpp @@ -17,7 +17,7 @@ auto ReplaceErrorIfPossible(T& result, const Token& pivotTok, ErrorCode newError return result.get_unexpected(); } -ExpressionParser::ExpressionParser(const Settings& /* settings */) +ExpressionParser::ExpressionParser(const Settings& /* settings */, TemplateEnv* /* env */) { } @@ -63,13 +63,10 @@ ExpressionParser::ParseResult> E if (includeIfPart && lexer.EatIfEqual(Keyword::If)) { - if (includeIfPart) - { - auto ifExpr = ParseIfExpression(lexer); - if (!ifExpr) - return ifExpr.get_unexpected(); - evaluator->SetTester(*ifExpr); - } + auto ifExpr = ParseIfExpression(lexer); + if (!ifExpr) + return ifExpr.get_unexpected(); + evaluator->SetTester(*ifExpr); } saver.Commit(); diff --git a/src/expression_parser.h b/src/expression_parser.h index a8c98a59..5273e9c7 100644 --- a/src/expression_parser.h +++ b/src/expression_parser.h @@ -17,7 +17,7 @@ class ExpressionParser template using ParseResult = nonstd::expected; - ExpressionParser(const Settings& settings); + explicit ExpressionParser(const Settings& settings, TemplateEnv* env = nullptr); ParseResult Parse(LexScanner& lexer); ParseResult> ParseFullExpression(LexScanner& lexer, bool includeIfPart = true); ParseResult ParseCallParams(LexScanner& lexer); diff --git a/src/filesystem_handler.cpp b/src/filesystem_handler.cpp index 744aefe4..9cf1c087 100644 --- a/src/filesystem_handler.cpp +++ b/src/filesystem_handler.cpp @@ -71,7 +71,7 @@ WCharFileStreamPtr MemoryFileSystem::OpenWStream(const std::string& name) const } RealFileSystem::RealFileSystem(std::string rootFolder) - : m_rootFolder(rootFolder) + : m_rootFolder(std::move(rootFolder)) { } diff --git a/src/filters.cpp b/src/filters.cpp index 9b53fdfa..a11e2168 100644 --- a/src/filters.cpp +++ b/src/filters.cpp @@ -537,10 +537,8 @@ SequenceAccessor::SequenceAccessor(FilterParams params, SequenceAccessor::Mode m case LengthMode: break; case MaxItemMode: - ParseParams({{"case_sensitive", false, InternalValue(false)}, {"attribute", false}}, params); - break; - case MinItemMode: - ParseParams({{"case_sensitive", false, InternalValue(false)}, {"attribute", false}}, params); + case MinItemMode: + ParseParams({{"case_sensitive", false, InternalValue(false)}, {"attribute", false}}, params); break; case RandomMode: case ReverseMode: diff --git a/src/function_base.h b/src/function_base.h index 5be168c3..2a1dd7ff 100644 --- a/src/function_base.h +++ b/src/function_base.h @@ -11,7 +11,7 @@ class FunctionBase public: protected: bool ParseParams(const std::initializer_list& argsInfo, const CallParams& params); - InternalValue GetArgumentValue(std::string argName, RenderContext& context, InternalValue defVal = InternalValue()); + InternalValue GetArgumentValue(const std::string& argName, RenderContext& context, InternalValue defVal = InternalValue()); protected: ParsedArguments m_args; @@ -26,7 +26,7 @@ inline bool FunctionBase::ParseParams(const std::initializer_list& return result; } -inline InternalValue FunctionBase::GetArgumentValue(std::string argName, RenderContext& context, InternalValue defVal) +inline InternalValue FunctionBase::GetArgumentValue(const std::string& argName, RenderContext& context, InternalValue defVal) { auto argExpr = m_args[argName]; return argExpr ? argExpr->Evaluate(context) : std::move(defVal); diff --git a/src/internal_value.h b/src/internal_value.h index fda704e8..44404771 100644 --- a/src/internal_value.h +++ b/src/internal_value.h @@ -218,7 +218,7 @@ class ListAdapter InternalValueList ToValueList() const; GenericList CreateGenericList() const { - if (m_accessorProvider && m_accessorProvider) + if (m_accessorProvider && m_accessorProvider()) return m_accessorProvider()->CreateGenericList(); return GenericList(); diff --git a/src/lexertk.h b/src/lexertk.h index 87091d63..7bb9fafa 100644 --- a/src/lexertk.h +++ b/src/lexertk.h @@ -349,7 +349,8 @@ namespace lexertk token() : type(e_none), - position(std::numeric_limits::max()) + position(std::numeric_limits::max()), + length(0) {} void clear() @@ -498,7 +499,7 @@ namespace lexertk s_end_ = 0; token_list_.clear(); token_itr_ = token_list_.end(); - store_token_itr_ = token_list_.end(); + store_token_itr_ = token_itr_; } inline bool process(const std::basic_string& str) @@ -542,7 +543,7 @@ namespace lexertk inline void begin() { token_itr_ = token_list_.begin(); - store_token_itr_ = token_list_.begin(); + store_token_itr_ = token_itr_; } inline void store() @@ -867,7 +868,7 @@ namespace lexertk if (endChar == *s_itr_) break; } - else if (escaped) + else escaped = false; ++s_itr_; diff --git a/src/statements.cpp b/src/statements.cpp index 2f4095f0..05373022 100644 --- a/src/statements.cpp +++ b/src/statements.cpp @@ -363,13 +363,28 @@ void IncludeStatement::Render(OutStream& os, RenderContext& values) auto doRender = [this, &values, &os](auto&& name) -> bool { auto tpl = values.GetRendererCallback()->LoadTemplate(name); - auto renderer = VisitTemplateImpl(tpl, false, [this](auto tplPtr) { - return CreateTemplateRenderer(tplPtr, m_withContext); - }); - if (renderer) + + try + { + auto renderer = VisitTemplateImpl(tpl, true, [this](auto tplPtr) { + return CreateTemplateRenderer(tplPtr, m_withContext); + }); + + if (renderer) + { + renderer->Render(os, values); + return true; + } + } + catch (const ErrorInfoTpl& err) + { + if (err.GetCode() != ErrorCode::FileNotFound) + throw; + } + catch (const ErrorInfoTpl& err) { - renderer->Render(os, values); - return true; + if (err.GetCode() != ErrorCode::FileNotFound) + throw; } return false; diff --git a/src/statements.h b/src/statements.h index 2461b436..273a220f 100644 --- a/src/statements.h +++ b/src/statements.h @@ -44,12 +44,12 @@ class ForStatement : public Statement void SetMainBody(RendererPtr renderer) { - m_mainBody = renderer; + m_mainBody = std::move(renderer); } void SetElseBody(RendererPtr renderer) { - m_elseBody = renderer; + m_elseBody = std::move(renderer); } void Render(OutStream& os, RenderContext& values) override; @@ -80,7 +80,7 @@ class IfStatement : public Statement void SetMainBody(RendererPtr renderer) { - m_mainBody = renderer; + m_mainBody = std::move(renderer); } void AddElseBranch(StatementPtr branch) @@ -110,7 +110,7 @@ class ElseBranchStatement : public Statement bool ShouldRender(RenderContext& values) const; void SetMainBody(RendererPtr renderer) { - m_mainBody = renderer; + m_mainBody = std::move(renderer); } void Render(OutStream& os, RenderContext& values) override; @@ -131,7 +131,7 @@ class SetStatement : public Statement void SetAssignmentExpr(ExpressionEvaluatorPtr<> expr) { - m_expr = expr; + m_expr = std::move(expr); } void Render(OutStream& os, RenderContext& values) override; @@ -153,7 +153,7 @@ class ParentBlockStatement : public Statement void SetMainBody(RendererPtr renderer) { - m_mainBody = renderer; + m_mainBody = std::move(renderer); } void Render(OutStream &os, RenderContext &values) override; @@ -177,7 +177,7 @@ class BlockStatement : public Statement void SetMainBody(RendererPtr renderer) { - m_mainBody = renderer; + m_mainBody = std::move(renderer); } void Render(OutStream &os, RenderContext &values) override; @@ -223,7 +223,7 @@ class IncludeStatement : public Statement void SetIncludeNamesExpr(ExpressionEvaluatorPtr<> expr) { - m_expr = expr; + m_expr = std::move(expr); } void Render(OutStream& os, RenderContext& values) override; @@ -244,7 +244,7 @@ class ImportStatement : public Statement void SetImportNameExpr(ExpressionEvaluatorPtr<> expr) { - m_nameExpr = expr; + m_nameExpr = std::move(expr); } void SetNamespace(std::string name) @@ -283,7 +283,7 @@ class MacroStatement : public Statement void SetMainBody(RendererPtr renderer) { - m_mainBody = renderer; + m_mainBody = std::move(renderer); } void Render(OutStream &os, RenderContext &values) override; @@ -347,7 +347,7 @@ class WithStatement : public Statement } void SetMainBody(RendererPtr renderer) { - m_mainBody = renderer; + m_mainBody = std::move(renderer); } void Render(OutStream &os, RenderContext &values) override; diff --git a/src/template_env.cpp b/src/template_env.cpp index 134cd3aa..4f912312 100644 --- a/src/template_env.cpp +++ b/src/template_env.cpp @@ -52,7 +52,7 @@ auto LoadTemplateImpl(TemplateEnv* env, std::string fileName, const T& filesyste auto stream = Functions::LoadFile(fileName, fh.handler.get()); if (stream) { - auto res = tpl.Load(*stream); + auto res = tpl.Load(*stream, fileName); if (!res) return ResultType(res.get_unexpected()); diff --git a/src/template_impl.h b/src/template_impl.h index e61568bb..e960c75b 100644 --- a/src/template_impl.h +++ b/src/template_impl.h @@ -111,7 +111,7 @@ class TemplateImpl : public ITemplateImpl { m_template = std::move(tpl); m_templateName = tplName.empty() ? std::string("noname.j2tpl") : std::move(tplName); - TemplateParser parser(&m_template, m_settings, m_templateName); + TemplateParser parser(&m_template, m_settings, m_env, m_templateName); auto parseResult = parser.Parse(); if (!parseResult) @@ -203,7 +203,7 @@ class TemplateImpl : public ITemplateImpl if (!name) { typename ErrorInfoTpl::Data errorData; - errorData.code = ErrorCode::UnexpectedException; + errorData.code = ErrorCode::InvalidTemplateName; errorData.srcLoc.col = 1; errorData.srcLoc.line = 1; errorData.srcLoc.fileName = m_templateName; diff --git a/src/template_parser.cpp b/src/template_parser.cpp index bddd6f7b..37a6a1a5 100644 --- a/src/template_parser.cpp +++ b/src/template_parser.cpp @@ -378,7 +378,7 @@ StatementsParser::ParseResult StatementsParser::ParseBlock(LexScanner& lexer, St } StatementInfo statementInfo = StatementInfo::Create(blockType, stmtTok); - statementInfo.renderer = blockRenderer; + statementInfo.renderer = std::move(blockRenderer); statementsInfo.push_back(statementInfo); return ParseResult(); } @@ -427,6 +427,9 @@ StatementsParser::ParseResult StatementsParser::ParseExtends(LexScanner& lexer, if (statementsInfo.empty()) return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok); + if (!m_env) + return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok); + Token tok = lexer.NextToken(); if (tok != Token::String && tok != Token::Identifier) { @@ -509,7 +512,7 @@ nonstd::expected StatementsParser::ParseMacroParams(Lex MacroParam p; p.paramName = AsString(name.value); - p.defaultValue = defVal; + p.defaultValue = std::move(defVal); items.push_back(std::move(p)); } while (lexer.EatIfEqual(',')); @@ -662,6 +665,8 @@ StatementsParser::ParseResult StatementsParser::ParseInclude(LexScanner& lexer, return MakeParseErrorTL(ErrorCode::UnexpectedToken, nextTok, Token::Eof, Token::Ignore, Token::With, Token::Without); } + if (!m_env && !isIgnoreMissing) + return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok); auto renderer = std::make_shared(isIgnoreMissing, isWithContext); renderer->SetIncludeNamesExpr(valueExpr); @@ -672,6 +677,9 @@ StatementsParser::ParseResult StatementsParser::ParseInclude(LexScanner& lexer, StatementsParser::ParseResult StatementsParser::ParseImport(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok) { + if (!m_env) + return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok); + ExpressionEvaluatorPtr<> valueExpr; ExpressionParser exprParser(m_settings); auto expr = exprParser.ParseFullExpression(lexer); @@ -719,6 +727,9 @@ StatementsParser::ParseResult StatementsParser::ParseImport(LexScanner& lexer, S StatementsParser::ParseResult StatementsParser::ParseFrom(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok) { + if (!m_env) + return MakeParseError(ErrorCode::TemplateEnvAbsent, stmtTok); + ExpressionEvaluatorPtr<> valueExpr; ExpressionParser exprParser(m_settings); auto expr = exprParser.ParseFullExpression(lexer); @@ -806,7 +817,7 @@ StatementsParser::ParseResult StatementsParser::ParseFrom(LexScanner& lexer, Sta return ParseResult(); } -StatementsParser::ParseResult StatementsParser::ParseDo(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok) +StatementsParser::ParseResult StatementsParser::ParseDo(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& /*stmtTok*/) { ExpressionEvaluatorPtr<> valueExpr; ExpressionParser exprParser(m_settings); @@ -859,7 +870,7 @@ StatementsParser::ParseResult StatementsParser::ParseWith(LexScanner& lexer, Sta return ParseResult(); } -StatementsParser::ParseResult StatementsParser::ParseEndWith(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& stmtTok) +StatementsParser::ParseResult StatementsParser::ParseEndWith(LexScanner& /*lexer*/, StatementInfoList& statementsInfo, const Token& stmtTok) { if (statementsInfo.size() <= 1) return MakeParseError(ErrorCode::UnexpectedStatement, stmtTok); diff --git a/src/template_parser.h b/src/template_parser.h index 3351b240..69f0e418 100644 --- a/src/template_parser.h +++ b/src/template_parser.h @@ -195,8 +195,9 @@ class StatementsParser public: using ParseResult = nonstd::expected; - StatementsParser(const Settings& settings) + StatementsParser(const Settings& settings, TemplateEnv* env) : m_settings(settings) + , m_env(env) {} ParseResult Parse(LexScanner& lexer, StatementInfoList& statementsInfo); @@ -225,6 +226,7 @@ class StatementsParser private: Settings m_settings; + TemplateEnv* m_env; ParseResult ParseWith(LexScanner& lexer, StatementInfoList& statementsInfo, const Token& token); @@ -241,10 +243,11 @@ class TemplateParser : public LexerHelper using ErrorInfo = ErrorInfoTpl; using ParseResult = nonstd::expected>; - TemplateParser(const string_t* tpl, const Settings& setts, std::string tplName) + TemplateParser(const string_t* tpl, const Settings& setts, TemplateEnv* env, std::string tplName) : m_template(tpl) , m_templateName(std::move(tplName)) , m_settings(setts) + , m_env(env) , m_roughTokenizer(traits_t::GetRoughTokenizer()) , m_keywords(traits_t::GetKeywords()) { @@ -545,7 +548,7 @@ class TemplateParser : public LexerHelper if (!lexer.Preprocess()) return MakeParseError(ErrorCode::Unspecified, MakeToken(Token::Unknown, {range.startOffset, range.startOffset + 1})); - P praser(m_settings); + P praser(m_settings, m_env); LexScanner scanner(lexer); auto result = praser.Parse(scanner, std::forward(args)...); if (!result) @@ -777,12 +780,13 @@ class TemplateParser : public LexerHelper const string_t* m_template; std::string m_templateName; const Settings& m_settings; + TemplateEnv* m_env = nullptr; std::basic_regex m_roughTokenizer; std::basic_regex m_keywords; std::vector m_lines; std::vector m_textBlocks; - LineInfo m_currentLineInfo; - TextBlockInfo m_currentBlockInfo; + LineInfo m_currentLineInfo = {}; + TextBlockInfo m_currentBlockInfo = {}; }; template diff --git a/src/testers.cpp b/src/testers.cpp index bf1b41df..7394a188 100644 --- a/src/testers.cpp +++ b/src/testers.cpp @@ -210,14 +210,14 @@ bool ValueTester::Test(const InternalValue& baseVal, RenderContext& context) if (valKind == ValueKind::Integer) { auto intVal = ConvertToInt(val); - result = (testMode == (intVal & 1)) == (EvenTest ? 0 : 1); + result = (intVal & 1) == (testMode == EvenTest ? 0 : 1); } else if (valKind == ValueKind::Double) { auto dblVal = ConvertToDouble(val); int64_t intVal = static_cast(dblVal); if (dblVal == intVal) - result = (testMode == (intVal & 1)) == (EvenTest ? 0 : 1); + result = (intVal & 1) == (testMode == EvenTest ? 0 : 1); } return result; }; diff --git a/src/value_visitors.h b/src/value_visitors.h index da1f4095..75d01548 100644 --- a/src/value_visitors.h +++ b/src/value_visitors.h @@ -345,7 +345,7 @@ struct UnaryOperation : BaseVisitor switch (m_oper) { case jinja2::UnaryExpression::LogicalNot: - result = val ? false : true; + result = fabs(val) > std::numeric_limits::epsilon() ? false : true; break; case jinja2::UnaryExpression::UnaryPlus: result = +val; diff --git a/test/errors_test.cpp b/test/errors_test.cpp index b635a424..d88aa32e 100644 --- a/test/errors_test.cpp +++ b/test/errors_test.cpp @@ -11,13 +11,106 @@ struct ErrorsGenericExtensionTestTag; using ErrorsGenericTest = InputOutputPairTest; using ErrorsGenericExtensionsTest = InputOutputPairTest; +std::string ErrorToString(const jinja2::ErrorInfo& error) +{ + std::ostringstream errorDescr; + errorDescr << error; + return errorDescr.str(); +} + +TEST_F(TemplateEnvFixture, EnvironmentAbsentErrorsTest) +{ + Template tpl1; + auto parseResult = tpl1.Load("{% extends 'module' %}"); + ASSERT_FALSE(parseResult.has_value()); + + EXPECT_EQ("noname.j2tpl:1:4: error: Template environment doesn't set\n{% extends 'module' %}\n---^-------", ErrorToString(parseResult.error())); + + parseResult = tpl1.Load("{% include 'module' %}"); + ASSERT_FALSE(parseResult.has_value()); + + EXPECT_EQ("noname.j2tpl:1:4: error: Template environment doesn't set\n{% include 'module' %}\n---^-------", ErrorToString(parseResult.error())); + + parseResult = tpl1.Load("{% from 'module' %}"); + ASSERT_FALSE(parseResult.has_value()); + + EXPECT_EQ("noname.j2tpl:1:4: error: Template environment doesn't set\n{% from 'module' %}\n---^-------", ErrorToString(parseResult.error())); + + parseResult = tpl1.Load("{% import 'module' %}"); + ASSERT_FALSE(parseResult.has_value()); + + EXPECT_EQ("noname.j2tpl:1:4: error: Template environment doesn't set\n{% import 'module' %}\n---^-------", ErrorToString(parseResult.error())); +} + +TEST_F(TemplateEnvFixture, RenderErrorsTest) +{ + Template tpl1; + auto renderResult = tpl1.RenderAsString({}); + ASSERT_FALSE(renderResult.has_value()); + + EXPECT_EQ(":1:1: error: Template not parsed\n", ErrorToString(renderResult.error())); + + Template tpl2; + tpl2.Load(R"({{ foo() }})"); + renderResult = tpl2.RenderAsString({{"foo", MakeCallable([]() -> Value {throw std::runtime_error("Bang!"); })}}); + ASSERT_FALSE(renderResult.has_value()); + + EXPECT_EQ("noname.j2tpl:1:1: error: Unexpected exception occurred during template processing. Exception: Bang!\n", ErrorToString(renderResult.error())); + + Template tpl3(&m_env); + auto parseResult = tpl3.Load("{% import name as name %}"); + EXPECT_TRUE(parseResult.has_value()); + if (!parseResult) + std::cout << parseResult.error() << std::endl; + renderResult = tpl3.RenderAsString({{"name", 10}}); + ASSERT_FALSE(renderResult.has_value()); + + EXPECT_EQ("noname.j2tpl:1:1: error: Invalid template name: 10\n", ErrorToString(renderResult.error())); +} + +TEST_F(TemplateEnvFixture, ErrorPropagationTest) +{ + AddFile("module", "{% for %}"); + Template tpl1(&m_env); + auto parseResult = tpl1.Load("{% extends 'module' %}"); + ASSERT_TRUE(parseResult.has_value()); + auto renderResult = tpl1.RenderAsString({}); + ASSERT_FALSE(renderResult.has_value()); + + EXPECT_EQ("module:1:8: error: Identifier expected\n{% for %}\n ---^-------", ErrorToString(renderResult.error())); + + Template tpl2(&m_env); + parseResult = tpl2.Load("{% include 'module' %}"); + ASSERT_TRUE(parseResult.has_value()); + renderResult = tpl2.RenderAsString({}); + ASSERT_FALSE(renderResult.has_value()); + + EXPECT_EQ("module:1:8: error: Identifier expected\n{% for %}\n ---^-------", ErrorToString(renderResult.error())); + + Template tpl3(&m_env); + parseResult = tpl3.Load("{% from 'module' import name %}"); + ASSERT_TRUE(parseResult.has_value()); + renderResult = tpl3.RenderAsString({}); + ASSERT_FALSE(renderResult.has_value()); + + EXPECT_EQ("module:1:8: error: Identifier expected\n{% for %}\n ---^-------", ErrorToString(renderResult.error())); + + Template tpl4(&m_env); + parseResult = tpl4.Load("{% import 'module' as module %}"); + ASSERT_TRUE(parseResult.has_value()); + renderResult = tpl4.RenderAsString({}); + ASSERT_FALSE(renderResult.has_value()); + + EXPECT_EQ("module:1:8: error: Identifier expected\n{% for %}\n ---^-------", ErrorToString(renderResult.error())); +} TEST_P(ErrorsGenericTest, Test) { auto& testParam = GetParam(); std::string source = testParam.tpl; - Template tpl; + TemplateEnv env; + Template tpl(&env); auto parseResult = tpl.Load(source); ASSERT_FALSE(parseResult.has_value()); @@ -277,8 +370,8 @@ INSTANTIATE_TEST_CASE_P(StatementsTest_2, ErrorsGenericTest, ::testing::Values( // "noname.j2tpl:1:4: error: Extension disabled\n{% do 'Hello World' %}\n---^-------"}, InputOutputPair{"{% with a = 42 %}{% endfor %}", "noname.j2tpl:1:21: error: Unexpected statement: 'endfor'\n{% with a = 42 %}{% endfor %}\n ---^-------"}, - InputOutputPair{"{% set a = 42 %}{% endwith %}", - "noname.j2tpl:1:20: error: Unexpected statement: 'endwith'\n{% set a = 42 %}{% endwith %}\n ---^-------"}, + InputOutputPair{"{% if a == 42 %}{% endwith %}", + "noname.j2tpl:1:20: error: Unexpected statement: 'endwith'\n{% if a == 42 %}{% endwith %}\n ---^-------"}, InputOutputPair{"{{}}", "noname.j2tpl:1:3: error: Unexpected token: '<>'\n{{}}\n--^-------"} )); diff --git a/thirdparty/internal_deps.cmake b/thirdparty/internal_deps.cmake index a12287d0..5cd58d60 100644 --- a/thirdparty/internal_deps.cmake +++ b/thirdparty/internal_deps.cmake @@ -11,7 +11,7 @@ add_library(optional-lite ALIAS optional-lite) update_submodule(nonstd/value-ptr-lite) add_subdirectory(thirdparty/nonstd/value-ptr-lite EXCLUDE_FROM_ALL) -add_library(value-ptr-lite ALIAS value_ptr-lite) +add_library(value-ptr-lite ALIAS value-ptr-lite) install (FILES thirdparty/nonstd/expected-lite/include/nonstd/expected.hpp diff --git a/thirdparty/nonstd/expected-lite b/thirdparty/nonstd/expected-lite index ce6d1847..d21c0869 160000 --- a/thirdparty/nonstd/expected-lite +++ b/thirdparty/nonstd/expected-lite @@ -1 +1 @@ -Subproject commit ce6d1847c5c9a80a707703f45ceda8d4ca3d2760 +Subproject commit d21c086934ec664e350d5301b240f43ba203f342 diff --git a/thirdparty/nonstd/optional-lite b/thirdparty/nonstd/optional-lite index 86db12f3..4c4da907 160000 --- a/thirdparty/nonstd/optional-lite +++ b/thirdparty/nonstd/optional-lite @@ -1 +1 @@ -Subproject commit 86db12f3b6c2026ad83efd51c5ce3622bb06f1b8 +Subproject commit 4c4da9074ac4c779bb81332fd82292edd8a6089d diff --git a/thirdparty/nonstd/value-ptr-lite b/thirdparty/nonstd/value-ptr-lite index 49165a36..2fd703f8 160000 --- a/thirdparty/nonstd/value-ptr-lite +++ b/thirdparty/nonstd/value-ptr-lite @@ -1 +1 @@ -Subproject commit 49165a36361692d3414d2495bcd859e67784e8ef +Subproject commit 2fd703f8a8e6a6b0bd210f12313d4f18530ff0c3 diff --git a/thirdparty/nonstd/variant-lite b/thirdparty/nonstd/variant-lite index fd33ea5e..7558be05 160000 --- a/thirdparty/nonstd/variant-lite +++ b/thirdparty/nonstd/variant-lite @@ -1 +1 @@ -Subproject commit fd33ea5ee4f152d9c5e85b79b3f40c705b932015 +Subproject commit 7558be05c20b9636512ead83e510b8dabf61870a