diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2018-01-08 14:46:44 -0800 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2018-01-09 17:27:02 -0800 |
commit | 0eb4b5cc0ec494f4b8de6933d2eddf97b12a77f7 (patch) | |
tree | 03c1263e633ccd89ca3b0203214cc56e2ff6b193 /src | |
parent | e887cda986218ae84317b3ecb5ba81d9b9bb776f (diff) | |
download | qtlocation-mapboxgl-0eb4b5cc0ec494f4b8de6933d2eddf97b12a77f7.tar.gz |
[core] Omit inferred type annotations for 'coalesce' arguments
Diffstat (limited to 'src')
-rw-r--r-- | src/mbgl/style/expression/coalesce.cpp | 22 | ||||
-rw-r--r-- | src/mbgl/style/expression/parsing_context.cpp | 59 |
2 files changed, 50 insertions, 31 deletions
diff --git a/src/mbgl/style/expression/coalesce.cpp b/src/mbgl/style/expression/coalesce.cpp index bfde3c7581..0373c9626c 100644 --- a/src/mbgl/style/expression/coalesce.cpp +++ b/src/mbgl/style/expression/coalesce.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/expression/coalesce.hpp> +#include <mbgl/style/expression/check_subtype.hpp> namespace mbgl { namespace style { @@ -36,14 +37,15 @@ ParseResult Coalesce::parse(const Convertible& value, ParsingContext& ctx) { } optional<type::Type> outputType; - if (ctx.getExpected() && *ctx.getExpected() != type::Value) { - outputType = ctx.getExpected(); + optional<type::Type> expectedType = ctx.getExpected(); + if (expectedType && *expectedType != type::Value) { + outputType = expectedType; } Coalesce::Args args; args.reserve(length - 1); for (std::size_t i = 1; i < length; i++) { - auto parsed = ctx.parse(arrayMember(value, i), i, outputType); + auto parsed = ctx.parse(arrayMember(value, i), i, outputType, ParsingContext::omitTypeAnnotations); if (!parsed) { return parsed; } @@ -52,9 +54,19 @@ ParseResult Coalesce::parse(const Convertible& value, ParsingContext& ctx) { } args.push_back(std::move(*parsed)); } - assert(outputType); - return ParseResult(std::make_unique<Coalesce>(*outputType, std::move(args))); + + // Above, we parse arguments without inferred type annotation so that + // they don't produce a runtime error for `null` input, which would + // preempt the desired null-coalescing behavior. + // Thus, if any of our arguments would have needed an annotation, we + // need to wrap the enclosing coalesce expression with it instead. + bool needsAnnotation = expectedType && + std::any_of(args.begin(), args.end(), [&] (const auto& arg) { + return type::checkSubtype(*expectedType, arg->getType()); + }); + + return ParseResult(std::make_unique<Coalesce>(needsAnnotation ? type::Value : *outputType, std::move(args))); } } // namespace expression diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp index acf3e63a48..0215982209 100644 --- a/src/mbgl/style/expression/parsing_context.cpp +++ b/src/mbgl/style/expression/parsing_context.cpp @@ -57,12 +57,15 @@ bool isConstant(const Expression& expression) { using namespace mbgl::style::conversion; -ParseResult ParsingContext::parse(const Convertible& value, std::size_t index_, optional<type::Type> expected_) { +ParseResult ParsingContext::parse(const Convertible& value, + std::size_t index_, + optional<type::Type> expected_, + TypeAnnotationOption typeAnnotationOption) { ParsingContext child(key + "[" + util::toString(index_) + "]", errors, std::move(expected_), scope); - return child.parse(value); + return child.parse(value, typeAnnotationOption); } ParseResult ParsingContext::parse(const Convertible& value, std::size_t index_, optional<type::Type> expected_, @@ -100,8 +103,7 @@ const ExpressionRegistry& getExpressionRegistry() { return registry; } -ParseResult ParsingContext::parse(const Convertible& value) -{ +ParseResult ParsingContext::parse(const Convertible& value, TypeAnnotationOption typeAnnotationOption) { ParseResult parsed; if (isArray(value)) { @@ -131,39 +133,44 @@ ParseResult ParsingContext::parse(const Convertible& value) } else { parsed = Literal::parse(value, *this); } - + if (!parsed) { assert(errors->size() > 0); - } else if (expected) { - auto wrapForType = [&](const type::Type& target, std::unique_ptr<Expression> expression) -> std::unique_ptr<Expression> { + return parsed; + } + + if (expected) { + auto array = [&](std::unique_ptr<Expression> expression) { std::vector<std::unique_ptr<Expression>> args; args.push_back(std::move(expression)); - if (target == type::Color) { - return std::make_unique<Coercion>(target, std::move(args)); - } else { - return std::make_unique<Assertion>(target, std::move(args)); - } + return args; }; - + const type::Type actual = (*parsed)->getType(); - if (*expected == type::Color && (actual == type::String || actual == type::Value)) { - parsed = wrapForType(type::Color, std::move(*parsed)); + if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean) && actual == type::Value) { + if (typeAnnotationOption == includeTypeAnnotations) { + parsed = { std::make_unique<Assertion>(*expected, array(std::move(*parsed))) }; + } } else if (expected->is<type::Array>() && actual == type::Value) { - parsed = { std::make_unique<ArrayAssertion>(expected->get<type::Array>(), std::move(*parsed)) }; - } else if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean) && actual == type::Value) { - parsed = wrapForType(*expected, std::move(*parsed)); - } - - checkType((*parsed)->getType()); - if (errors->size() > 0) { - return ParseResult(); + if (typeAnnotationOption == includeTypeAnnotations) { + parsed = { std::make_unique<ArrayAssertion>(expected->get<type::Array>(), std::move(*parsed)) }; + } + } else if (*expected == type::Color && (actual == type::Value || actual == type::String)) { + if (typeAnnotationOption == includeTypeAnnotations) { + parsed = { std::make_unique<Coercion>(*expected, array(std::move(*parsed))) }; + } + } else { + checkType((*parsed)->getType()); + if (errors->size() > 0) { + return ParseResult(); + } } } - + // If an expression's arguments are all literals, we can evaluate // it immediately and replace it with a literal value in the // parsed result. - if (parsed && !dynamic_cast<Literal *>(parsed->get()) && isConstant(**parsed)) { + if (!dynamic_cast<Literal *>(parsed->get()) && isConstant(**parsed)) { EvaluationContext params(nullptr); EvaluationResult evaluated((*parsed)->evaluate(params)); if (!evaluated) { @@ -185,7 +192,7 @@ ParseResult ParsingContext::parse(const Convertible& value) } // if this is the root expression, enforce constraints on the use ["zoom"]. - if (key.size() == 0 && parsed && !isZoomConstant(**parsed)) { + if (key.size() == 0 && !isZoomConstant(**parsed)) { optional<variant<const InterpolateBase*, const Step*, ParsingError>> zoomCurve = findZoomCurve(parsed->get()); if (!zoomCurve) { error(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)"); |