mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Restrict CSSDataTypeParser function and simple block parsing to block scope (#48768)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48768 Right now, if something like `CSSColor` accepted a function notation block (e.g. for `rgb()` syntax), it is given a syntax parser which may extend beyond the scope of the current function block. This is confusing, but also problematic for the `CSSSyntaxParser` block visiting logic to validate a correct scope exit. This change makes it so that the `CSSSyntaxParser` passsed to `CSSDataTypeParser` for function and simple blocks is limited to the syntax within the given block. This prevents extra visiblity beyond the block we are trying to parse, and allows the function/simple block visitor to reliably fail parsing if the block is not correctly terminated, or there are unconsumed component values within the block not handled by the data type parser. Changelog: [Internal] Reviewed By: joevilches Differential Revision: D68340127 fbshipit-source-id: 402f2eabdf6df7bce99223c966f22c2158103be5
This commit is contained in:
committed by
Facebook GitHub Bot
parent
135277ace1
commit
c33dd62afd
@@ -15,6 +15,8 @@
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
class CSSSyntaxParser;
|
||||
|
||||
/**
|
||||
* Describes context for a CSS function component value.
|
||||
*/
|
||||
@@ -41,9 +43,10 @@ struct CSSSimpleBlock {
|
||||
* of the function block.
|
||||
*/
|
||||
template <typename T, typename ReturnT>
|
||||
concept CSSFunctionVisitor = requires(T visitor, CSSFunctionBlock func) {
|
||||
{ visitor(func) } -> std::convertible_to<ReturnT>;
|
||||
};
|
||||
concept CSSFunctionVisitor =
|
||||
requires(T visitor, CSSFunctionBlock func, CSSSyntaxParser& blockParser) {
|
||||
{ visitor(func, blockParser) } -> std::convertible_to<ReturnT>;
|
||||
};
|
||||
|
||||
/**
|
||||
* A CSSPreservedTokenVisitor is called after parsing a preserved token
|
||||
@@ -61,9 +64,10 @@ concept CSSPreservedTokenVisitor =
|
||||
* of the block.
|
||||
*/
|
||||
template <typename T, typename ReturnT>
|
||||
concept CSSSimpleBlockVisitor = requires(T visitor, CSSSimpleBlock block) {
|
||||
{ visitor(block) } -> std::convertible_to<ReturnT>;
|
||||
};
|
||||
concept CSSSimpleBlockVisitor =
|
||||
requires(T visitor, CSSSimpleBlock block, CSSSyntaxParser& blockParser) {
|
||||
{ visitor(block, blockParser) } -> std::convertible_to<ReturnT>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Any visitor for a component value.
|
||||
@@ -164,6 +168,11 @@ class CSSSyntaxParser {
|
||||
}
|
||||
|
||||
private:
|
||||
CSSSyntaxParser(CSSSyntaxParser& parser, CSSTokenType terminator)
|
||||
: tokenizer_{parser.tokenizer_},
|
||||
currentToken_{parser.currentToken_},
|
||||
terminator_{terminator} {}
|
||||
|
||||
constexpr const CSSToken& peek() const {
|
||||
return currentToken_;
|
||||
}
|
||||
@@ -174,8 +183,14 @@ class CSSSyntaxParser {
|
||||
return prevToken;
|
||||
}
|
||||
|
||||
constexpr void advanceToBlockParser(CSSSyntaxParser& blockParser) {
|
||||
currentToken_ = blockParser.currentToken_;
|
||||
tokenizer_ = blockParser.tokenizer_;
|
||||
}
|
||||
|
||||
CSSTokenizer tokenizer_;
|
||||
CSSToken currentToken_;
|
||||
CSSTokenType terminator_{CSSTokenType::EndOfFile};
|
||||
};
|
||||
|
||||
template <typename ReturnT, CSSComponentValueVisitor<ReturnT>... VisitorsT>
|
||||
@@ -201,6 +216,10 @@ struct CSSComponentValueVisitorDispatcher {
|
||||
break;
|
||||
}
|
||||
|
||||
if (parser.peek().type() == parser.terminator_) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (parser.peek().type()) {
|
||||
case CSSTokenType::Function:
|
||||
if (auto ret = visitFunction(visitors...)) {
|
||||
@@ -235,28 +254,22 @@ struct CSSComponentValueVisitorDispatcher {
|
||||
return ReturnT{};
|
||||
}
|
||||
|
||||
constexpr ReturnT consumeNextCommaDelimitedValue(
|
||||
const VisitorsT&... visitors) {
|
||||
parser.consumeWhitespace();
|
||||
if (parser.consumeToken().type() != CSSTokenType::Comma) {
|
||||
return {};
|
||||
}
|
||||
parser.consumeWhitespace();
|
||||
return consumeComponentValue(std::forward<VisitorsT>(visitors)...);
|
||||
}
|
||||
|
||||
constexpr ReturnT consumeNextWhitespaceDelimitedValue(
|
||||
const VisitorsT&... visitors) {
|
||||
parser.consumeWhitespace();
|
||||
return consumeComponentValue(std::forward<VisitorsT>(visitors)...);
|
||||
}
|
||||
|
||||
constexpr std::optional<ReturnT> visitFunction(
|
||||
const CSSComponentValueVisitor<ReturnT> auto& visitor,
|
||||
const CSSComponentValueVisitor<ReturnT> auto&... rest) {
|
||||
if constexpr (CSSFunctionVisitor<decltype(visitor), ReturnT>) {
|
||||
auto functionValue =
|
||||
visitor({.name = parser.consumeToken().stringValue()});
|
||||
auto name = parser.consumeToken().stringValue();
|
||||
|
||||
// CSS syntax spec says whitespace is a preserved token, but CSS values
|
||||
// spec allows whitespace around parens for all function notation, so we
|
||||
// allow this to let the visitors not need to deal with leading/trailing
|
||||
// whitespace. https://www.w3.org/TR/css-values-3/#functional-notations
|
||||
parser.consumeWhitespace();
|
||||
|
||||
auto blockParser =
|
||||
CSSSyntaxParser{parser, CSSTokenType::CloseParen /*terminator*/};
|
||||
auto functionValue = visitor({name}, blockParser);
|
||||
parser.advanceToBlockParser(blockParser);
|
||||
parser.consumeWhitespace();
|
||||
if (parser.peek().type() == CSSTokenType::CloseParen) {
|
||||
parser.consumeToken();
|
||||
@@ -273,16 +286,16 @@ struct CSSComponentValueVisitorDispatcher {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Can be one of std::monostate (variant null-type), CSSWideKeyword,
|
||||
// CSSLength, or CSSPercentage
|
||||
|
||||
constexpr std::optional<ReturnT> visitSimpleBlock(
|
||||
CSSTokenType endToken,
|
||||
const CSSComponentValueVisitor<ReturnT> auto& visitor,
|
||||
const CSSComponentValueVisitor<ReturnT> auto&... rest) {
|
||||
if constexpr (CSSSimpleBlockVisitor<decltype(visitor), ReturnT>) {
|
||||
auto blockValue =
|
||||
visitor({.openBracketType = parser.consumeToken().type()});
|
||||
auto openBracketType = parser.consumeToken().type();
|
||||
parser.consumeWhitespace();
|
||||
auto blockParser = CSSSyntaxParser{parser, endToken};
|
||||
auto blockValue = visitor({openBracketType}, blockParser);
|
||||
parser.advanceToBlockParser(blockParser);
|
||||
parser.consumeWhitespace();
|
||||
if (parser.peek().type() == endToken) {
|
||||
parser.consumeToken();
|
||||
|
||||
@@ -42,15 +42,15 @@ class CSSValueParser {
|
||||
ReturnT,
|
||||
CSSDataTypeParser<AllowedTypesT>...>(token);
|
||||
},
|
||||
[&](const CSSSimpleBlock& block) {
|
||||
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
|
||||
return tryConsumeSimpleBlock<
|
||||
ReturnT,
|
||||
CSSDataTypeParser<AllowedTypesT>...>(block);
|
||||
CSSDataTypeParser<AllowedTypesT>...>(block, blockParser);
|
||||
},
|
||||
[&](const CSSFunctionBlock& func) {
|
||||
[&](const CSSFunctionBlock& func, CSSSyntaxParser& blockParser) {
|
||||
return tryConsumeFunctionBlock<
|
||||
ReturnT,
|
||||
CSSDataTypeParser<AllowedTypesT>...>(func);
|
||||
CSSDataTypeParser<AllowedTypesT>...>(func, blockParser);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -90,7 +90,9 @@ class CSSValueParser {
|
||||
}
|
||||
|
||||
template <typename ReturnT>
|
||||
constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock& /*token*/) {
|
||||
constexpr ReturnT tryConsumeSimpleBlock(
|
||||
const CSSSimpleBlock& /*token*/,
|
||||
CSSSyntaxParser& /*blockParser*/) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -98,18 +100,22 @@ class CSSValueParser {
|
||||
typename ReturnT,
|
||||
CSSValidDataTypeParser ParserT,
|
||||
CSSValidDataTypeParser... RestParserT>
|
||||
constexpr ReturnT tryConsumeSimpleBlock(const CSSSimpleBlock& block) {
|
||||
constexpr ReturnT tryConsumeSimpleBlock(
|
||||
const CSSSimpleBlock& block,
|
||||
CSSSyntaxParser& blockParser) {
|
||||
if constexpr (CSSSimpleBlockSink<ParserT>) {
|
||||
if (auto ret = ParserT::consumeSimpleBlock(block, parser_)) {
|
||||
if (auto ret = ParserT::consumeSimpleBlock(block, blockParser)) {
|
||||
return *ret;
|
||||
}
|
||||
}
|
||||
|
||||
return tryConsumeSimpleBlock<ReturnT, RestParserT...>(block);
|
||||
return tryConsumeSimpleBlock<ReturnT, RestParserT...>(block, blockParser);
|
||||
}
|
||||
|
||||
template <typename ReturnT>
|
||||
constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock& /*func*/) {
|
||||
constexpr ReturnT tryConsumeFunctionBlock(
|
||||
const CSSFunctionBlock& /*func*/,
|
||||
CSSSyntaxParser& /*blockParser*/) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -117,14 +123,16 @@ class CSSValueParser {
|
||||
typename ReturnT,
|
||||
CSSValidDataTypeParser ParserT,
|
||||
CSSValidDataTypeParser... RestParserT>
|
||||
constexpr ReturnT tryConsumeFunctionBlock(const CSSFunctionBlock& func) {
|
||||
constexpr ReturnT tryConsumeFunctionBlock(
|
||||
const CSSFunctionBlock& func,
|
||||
CSSSyntaxParser& blockParser) {
|
||||
if constexpr (CSSFunctionBlockSink<ParserT>) {
|
||||
if (auto ret = ParserT::consumeFunctionBlock(func, parser_)) {
|
||||
if (auto ret = ParserT::consumeFunctionBlock(func, blockParser)) {
|
||||
return *ret;
|
||||
}
|
||||
}
|
||||
|
||||
return tryConsumeFunctionBlock<ReturnT, RestParserT...>(func);
|
||||
return tryConsumeFunctionBlock<ReturnT, RestParserT...>(func, blockParser);
|
||||
}
|
||||
|
||||
CSSSyntaxParser& parser_;
|
||||
|
||||
+108
-28
@@ -47,9 +47,15 @@ TEST(CSSSyntaxParser, single_function_no_args) {
|
||||
CSSSyntaxParser parser{"foo()"};
|
||||
|
||||
auto funcName = parser.consumeComponentValue<std::string_view>(
|
||||
[](const CSSFunctionBlock& function) {
|
||||
[](const CSSFunctionBlock& function, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(function.name, "foo");
|
||||
return function.name;
|
||||
|
||||
auto hasMoreTokens = blockParser.consumeComponentValue<bool>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
[](const CSSPreservedToken& /*token*/) { return true; });
|
||||
|
||||
EXPECT_FALSE(hasMoreTokens);
|
||||
});
|
||||
EXPECT_EQ(funcName, "foo");
|
||||
}
|
||||
@@ -58,12 +64,12 @@ TEST(CSSSyntaxParser, single_function_with_whitespace_delimited_args) {
|
||||
CSSSyntaxParser parser{"foo( a b c)"};
|
||||
|
||||
auto funcArgs = parser.consumeComponentValue<std::vector<std::string>>(
|
||||
[&](const CSSFunctionBlock& function) {
|
||||
[&](const CSSFunctionBlock& function, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(function.name, "foo");
|
||||
|
||||
std::vector<std::string> args;
|
||||
|
||||
args.emplace_back(parser.consumeComponentValue<std::string_view>(
|
||||
args.emplace_back(blockParser.consumeComponentValue<std::string_view>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
|
||||
[](const CSSPreservedToken& token) {
|
||||
@@ -72,7 +78,7 @@ TEST(CSSSyntaxParser, single_function_with_whitespace_delimited_args) {
|
||||
return token.stringValue();
|
||||
}));
|
||||
|
||||
args.emplace_back(parser.consumeComponentValue<std::string_view>(
|
||||
args.emplace_back(blockParser.consumeComponentValue<std::string_view>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
|
||||
[](const CSSPreservedToken& token) {
|
||||
@@ -81,7 +87,7 @@ TEST(CSSSyntaxParser, single_function_with_whitespace_delimited_args) {
|
||||
return token.stringValue();
|
||||
}));
|
||||
|
||||
args.emplace_back(parser.consumeComponentValue<std::string_view>(
|
||||
args.emplace_back(blockParser.consumeComponentValue<std::string_view>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
|
||||
[](const CSSPreservedToken& token) {
|
||||
@@ -90,6 +96,12 @@ TEST(CSSSyntaxParser, single_function_with_whitespace_delimited_args) {
|
||||
return token.stringValue();
|
||||
}));
|
||||
|
||||
auto hasMoreTokens = blockParser.consumeComponentValue<bool>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
[](const CSSPreservedToken& /*token*/) { return true; });
|
||||
|
||||
EXPECT_FALSE(hasMoreTokens);
|
||||
|
||||
return args;
|
||||
});
|
||||
|
||||
@@ -101,12 +113,12 @@ TEST(CSSSyntaxParser, single_function_with_comma_delimited_args) {
|
||||
CSSSyntaxParser parser{"rgb(100, 200, 50 )"};
|
||||
|
||||
auto funcArgs = parser.consumeComponentValue<std::array<uint8_t, 3>>(
|
||||
[&](const CSSFunctionBlock& function) {
|
||||
[&](const CSSFunctionBlock& function, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(function.name, "rgb");
|
||||
|
||||
std::array<uint8_t, 3> rgb{};
|
||||
|
||||
rgb[0] = parser.consumeComponentValue<uint8_t>(
|
||||
rgb[0] = blockParser.consumeComponentValue<uint8_t>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Number);
|
||||
@@ -114,7 +126,7 @@ TEST(CSSSyntaxParser, single_function_with_comma_delimited_args) {
|
||||
return static_cast<uint8_t>(token.numericValue());
|
||||
});
|
||||
|
||||
rgb[1] = parser.consumeComponentValue<uint8_t>(
|
||||
rgb[1] = blockParser.consumeComponentValue<uint8_t>(
|
||||
CSSComponentValueDelimiter::Comma,
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Number);
|
||||
@@ -122,7 +134,7 @@ TEST(CSSSyntaxParser, single_function_with_comma_delimited_args) {
|
||||
return static_cast<uint8_t>(token.numericValue());
|
||||
});
|
||||
|
||||
rgb[2] = parser.consumeComponentValue<uint8_t>(
|
||||
rgb[2] = blockParser.consumeComponentValue<uint8_t>(
|
||||
CSSComponentValueDelimiter::Comma,
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Number);
|
||||
@@ -130,6 +142,12 @@ TEST(CSSSyntaxParser, single_function_with_comma_delimited_args) {
|
||||
return static_cast<uint8_t>(token.numericValue());
|
||||
});
|
||||
|
||||
auto hasMoreTokens = blockParser.consumeComponentValue<bool>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
[](const CSSPreservedToken& /*token*/) { return true; });
|
||||
|
||||
EXPECT_FALSE(hasMoreTokens);
|
||||
|
||||
return rgb;
|
||||
});
|
||||
|
||||
@@ -141,9 +159,9 @@ TEST(CSSSyntaxParser, complex) {
|
||||
CSSSyntaxParser parser{"foo(a bar())baz() 12px"};
|
||||
|
||||
auto fooFunc = parser.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSFunctionBlock& function) {
|
||||
[&](const CSSFunctionBlock& function, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(function.name, "foo");
|
||||
auto identArg = parser.consumeComponentValue<std::string_view>(
|
||||
auto identArg = blockParser.consumeComponentValue<std::string_view>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Ident);
|
||||
@@ -152,20 +170,32 @@ TEST(CSSSyntaxParser, complex) {
|
||||
});
|
||||
EXPECT_EQ(identArg, "a");
|
||||
|
||||
auto barFunc = parser.consumeComponentValue<std::string_view>(
|
||||
auto barFunc = blockParser.consumeComponentValue<std::string_view>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
[&](const CSSFunctionBlock& function) {
|
||||
[&](const CSSFunctionBlock& function,
|
||||
CSSSyntaxParser& nestedBlockParser) {
|
||||
EXPECT_EQ(function.name, "bar");
|
||||
auto hasMoreTokens =
|
||||
nestedBlockParser.consumeComponentValue<bool>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
[](const CSSPreservedToken& /*token*/) { return true; });
|
||||
EXPECT_FALSE(hasMoreTokens);
|
||||
|
||||
return function.name;
|
||||
});
|
||||
EXPECT_EQ(barFunc, "bar");
|
||||
|
||||
auto hasMoreTokens = blockParser.consumeComponentValue<bool>(
|
||||
CSSComponentValueDelimiter::Whitespace,
|
||||
[](const CSSPreservedToken& /*token*/) { return true; });
|
||||
EXPECT_FALSE(hasMoreTokens);
|
||||
|
||||
return function.name;
|
||||
});
|
||||
EXPECT_EQ(fooFunc, "foo");
|
||||
|
||||
auto bazFunc = parser.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSFunctionBlock& function) {
|
||||
[&](const CSSFunctionBlock& function, CSSSyntaxParser& /*blockParser*/) {
|
||||
EXPECT_EQ(function.name, "baz");
|
||||
return function.name;
|
||||
});
|
||||
@@ -184,18 +214,22 @@ TEST(CSSSyntaxParser, complex) {
|
||||
|
||||
TEST(CSSSyntaxParser, unterminated_functions) {
|
||||
EXPECT_FALSE(CSSSyntaxParser{"foo("}.consumeComponentValue<bool>(
|
||||
[](const CSSFunctionBlock&) { return true; }));
|
||||
[](const CSSFunctionBlock&, CSSSyntaxParser& /*blockParser*/) {
|
||||
return true;
|
||||
}));
|
||||
|
||||
EXPECT_FALSE(CSSSyntaxParser{"foo(a bar()baz()"}.consumeComponentValue<bool>(
|
||||
[](const CSSFunctionBlock&) { return true; }));
|
||||
[](const CSSFunctionBlock&, CSSSyntaxParser& /*blockParser*/) {
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(CSSSyntaxParser, simple_blocks) {
|
||||
CSSSyntaxParser parser1{"(a)"};
|
||||
auto identValue = parser1.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSSimpleBlock& block) {
|
||||
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(block.openBracketType, CSSTokenType::OpenParen);
|
||||
return parser1.consumeComponentValue<std::string_view>(
|
||||
return blockParser.consumeComponentValue<std::string_view>(
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Ident);
|
||||
return token.stringValue();
|
||||
@@ -205,9 +239,9 @@ TEST(CSSSyntaxParser, simple_blocks) {
|
||||
|
||||
CSSSyntaxParser parser2{"[b ]"};
|
||||
auto identValue2 = parser2.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSSimpleBlock& block) {
|
||||
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(block.openBracketType, CSSTokenType::OpenSquare);
|
||||
return parser2.consumeComponentValue<std::string_view>(
|
||||
return blockParser.consumeComponentValue<std::string_view>(
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Ident);
|
||||
return token.stringValue();
|
||||
@@ -217,9 +251,9 @@ TEST(CSSSyntaxParser, simple_blocks) {
|
||||
|
||||
CSSSyntaxParser parser3{"{c}"};
|
||||
auto identValue3 = parser3.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSSimpleBlock& block) {
|
||||
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(block.openBracketType, CSSTokenType::OpenCurly);
|
||||
return parser3.consumeComponentValue<std::string_view>(
|
||||
return blockParser.consumeComponentValue<std::string_view>(
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Ident);
|
||||
return token.stringValue();
|
||||
@@ -231,9 +265,9 @@ TEST(CSSSyntaxParser, simple_blocks) {
|
||||
TEST(CSSSyntaxParser, unterminated_simple_blocks) {
|
||||
CSSSyntaxParser parser1{"(a"};
|
||||
auto identValue = parser1.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSSimpleBlock& block) {
|
||||
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(block.openBracketType, CSSTokenType::OpenParen);
|
||||
return parser1.consumeComponentValue<std::string_view>(
|
||||
return blockParser.consumeComponentValue<std::string_view>(
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Ident);
|
||||
return token.stringValue();
|
||||
@@ -243,9 +277,9 @@ TEST(CSSSyntaxParser, unterminated_simple_blocks) {
|
||||
|
||||
CSSSyntaxParser parser2{"[b "};
|
||||
auto identValue2 = parser2.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSSimpleBlock& block) {
|
||||
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(block.openBracketType, CSSTokenType::OpenSquare);
|
||||
return parser2.consumeComponentValue<std::string_view>(
|
||||
return blockParser.consumeComponentValue<std::string_view>(
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Ident);
|
||||
return token.stringValue();
|
||||
@@ -255,9 +289,9 @@ TEST(CSSSyntaxParser, unterminated_simple_blocks) {
|
||||
|
||||
CSSSyntaxParser parser3{"{c"};
|
||||
auto identValue3 = parser3.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSSimpleBlock& block) {
|
||||
[&](const CSSSimpleBlock& block, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(block.openBracketType, CSSTokenType::OpenCurly);
|
||||
return parser3.consumeComponentValue<std::string_view>(
|
||||
return blockParser.consumeComponentValue<std::string_view>(
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Ident);
|
||||
return token.stringValue();
|
||||
@@ -266,4 +300,50 @@ TEST(CSSSyntaxParser, unterminated_simple_blocks) {
|
||||
EXPECT_EQ(identValue3, "");
|
||||
}
|
||||
|
||||
TEST(CSSSyntaxParser, unconsumed_function_args) {
|
||||
CSSSyntaxParser parser{"foo(a)"};
|
||||
auto funcValue =
|
||||
parser.consumeComponentValue<std::optional<std::string_view>>(
|
||||
[&](const CSSFunctionBlock& function,
|
||||
CSSSyntaxParser& /*blockParser*/) {
|
||||
EXPECT_EQ(function.name, "foo");
|
||||
return function.name;
|
||||
});
|
||||
|
||||
EXPECT_EQ(funcValue, std::nullopt);
|
||||
}
|
||||
|
||||
TEST(CSSSyntaxParser, whitespace_surrounding_function_args) {
|
||||
CSSSyntaxParser parser{"foo( a )"};
|
||||
auto funcValue = parser.consumeComponentValue<std::string_view>(
|
||||
[&](const CSSFunctionBlock& function, CSSSyntaxParser& blockParser) {
|
||||
EXPECT_EQ(function.name, "foo");
|
||||
|
||||
auto identArg = blockParser.consumeComponentValue<std::string_view>(
|
||||
CSSComponentValueDelimiter::None,
|
||||
[](const CSSPreservedToken& token) {
|
||||
EXPECT_EQ(token.type(), CSSTokenType::Ident);
|
||||
EXPECT_EQ(token.stringValue(), "a");
|
||||
return token.stringValue();
|
||||
});
|
||||
|
||||
EXPECT_EQ(identArg, "a");
|
||||
|
||||
return function.name;
|
||||
});
|
||||
|
||||
EXPECT_EQ(funcValue, "foo");
|
||||
}
|
||||
|
||||
TEST(CSSSyntaxParser, unconsumed_simple_block_args) {
|
||||
CSSSyntaxParser parser{"{a}"};
|
||||
auto funcValue = parser.consumeComponentValue<std::optional<CSSTokenType>>(
|
||||
[&](const CSSSimpleBlock& block, CSSSyntaxParser& /*blockParser*/) {
|
||||
EXPECT_EQ(block.openBracketType, CSSTokenType::OpenCurly);
|
||||
return block.openBracketType;
|
||||
});
|
||||
|
||||
EXPECT_EQ(funcValue, std::nullopt);
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
Reference in New Issue
Block a user