viml/parser/expressions: Fix some errors spotted by KLEE

Not all of them are fixed yet though.
This commit is contained in:
ZyX 2017-10-19 10:48:05 +03:00
parent 895793fc82
commit 47938e1e22
2 changed files with 178 additions and 19 deletions

View File

@ -1091,7 +1091,7 @@ void viml_pexpr_free_ast(ExprAST ast)
// NVimInvalidDoubleQuote -> NVimInvalidString // NVimInvalidDoubleQuote -> NVimInvalidString
// NVimInvalidDoubleQuotedBody -> NVimInvalidString // NVimInvalidDoubleQuotedBody -> NVimInvalidString
// NVimInvalidDoubleQuotedEscape -> NVimInvalidStringSpecial // NVimInvalidDoubleQuotedEscape -> NVimInvalidStringSpecial
// NVimInvalidDoubleQuotedUnknownEscape -> NVimInvalid // NVimInvalidDoubleQuotedUnknownEscape -> NVimInvalidDoubleQuotedEscape
// //
// NVimFigureBrace -> NVimInternalError // NVimFigureBrace -> NVimInternalError
// NVimInvalidSingleQuotedUnknownEscape -> NVimInternalError // NVimInvalidSingleQuotedUnknownEscape -> NVimInternalError
@ -1313,7 +1313,7 @@ static bool viml_pexpr_handle_bop(const ParserState *const pstate,
/// ParserPosition literal based on ParserPosition pos with columns shifted /// ParserPosition literal based on ParserPosition pos with columns shifted
/// ///
/// Function does not check whether remaining position is valid. /// Function does not check whether resulting position is valid.
/// ///
/// @param[in] pos Position to shift. /// @param[in] pos Position to shift.
/// @param[in] shift Number of bytes to shift. /// @param[in] shift Number of bytes to shift.
@ -1326,6 +1326,21 @@ static inline ParserPosition shifted_pos(const ParserPosition pos,
return (ParserPosition) { .line = pos.line, .col = pos.col + shift }; return (ParserPosition) { .line = pos.line, .col = pos.col + shift };
} }
/// ParserPosition literal based on ParserPosition pos with specified column
///
/// Function does not check whether remaining position is valid.
///
/// @param[in] pos Position to adjust.
/// @param[in] new_col New column.
///
/// @return Shifted position.
static inline ParserPosition recol_pos(const ParserPosition pos,
const size_t new_col)
FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
{
return (ParserPosition) { .line = pos.line, .col = new_col };
}
/// Get highlight group name /// Get highlight group name
#define HL(g) (is_invalid ? "NVimInvalid" #g : "NVim" #g) #define HL(g) (is_invalid ? "NVimInvalid" #g : "NVim" #g)
@ -1639,7 +1654,7 @@ static void parse_quoted_string(ParserState *const pstate,
size_t n = (*p == 'u' ? 4 : 8); size_t n = (*p == 'u' ? 4 : 8);
int nr = 0; int nr = 0;
p++; p++;
while (n-- && ascii_isxdigit(p[1])) { while (p + 1 < e && n-- && ascii_isxdigit(p[1])) {
p++; p++;
nr = (nr << 4) + hex2nr(*p); nr = (nr << 4) + hex2nr(*p);
} }
@ -1659,7 +1674,7 @@ static void parse_quoted_string(ParserState *const pstate,
if (*p >= '0' && *p <= '7') { if (*p >= '0' && *p <= '7') {
size--; size--;
p++; p++;
if (*p >= '0' && *p <= '7') { if (p < e && *p >= '0' && *p <= '7') {
size--; size--;
p++; p++;
} }
@ -1715,7 +1730,7 @@ static void parse_quoted_string(ParserState *const pstate,
// Hexadecimal or unicode. // Hexadecimal or unicode.
case 'X': case 'x': case 'u': case 'U': { case 'X': case 'x': case 'u': case 'U': {
if (ascii_isxdigit(p[1])) { if (p + 1 < e && ascii_isxdigit(p[1])) {
size_t n; size_t n;
int nr; int nr;
bool is_hex = (*p == 'x' || *p == 'X'); bool is_hex = (*p == 'x' || *p == 'X');
@ -1728,7 +1743,7 @@ static void parse_quoted_string(ParserState *const pstate,
n = 8; n = 8;
} }
nr = 0; nr = 0;
while (n-- && ascii_isxdigit(p[1])) { while (p + 1 < e && n-- && ascii_isxdigit(p[1])) {
p++; p++;
nr = (nr << 4) + hex2nr(*p); nr = (nr << 4) + hex2nr(*p);
} }
@ -1749,9 +1764,9 @@ static void parse_quoted_string(ParserState *const pstate,
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '0': case '1': case '2': case '3': case '4': case '5': case '6':
case '7': { case '7': {
uint8_t ch = (uint8_t)(*p++ - '0'); uint8_t ch = (uint8_t)(*p++ - '0');
if (*p >= '0' && *p <= '7') { if (p < e && *p >= '0' && *p <= '7') {
ch = (uint8_t)((ch << 3) + *p++ - '0'); ch = (uint8_t)((ch << 3) + *p++ - '0');
if (*p >= '0' && *p <= '7') { if (p < e && *p >= '0' && *p <= '7') {
ch = (uint8_t)((ch << 3) + *p++ - '0'); ch = (uint8_t)((ch << 3) + *p++ - '0');
} }
} }
@ -1793,7 +1808,7 @@ static void parse_quoted_string(ParserState *const pstate,
// TODO(ZyX-I): use ast_stack to determine and highlight regular expressions // TODO(ZyX-I): use ast_stack to determine and highlight regular expressions
// TODO(ZyX-I): use ast_stack to determine and highlight printf format str // TODO(ZyX-I): use ast_stack to determine and highlight printf format str
// TODO(ZyX-I): use ast_stack to determine and highlight expression strings // TODO(ZyX-I): use ast_stack to determine and highlight expression strings
size_t next_col = 1; size_t next_col = token.start.col + 1;
const char *const body_str = (is_double const char *const body_str = (is_double
? HL(DoubleQuotedBody) ? HL(DoubleQuotedBody)
: HL(SingleQuotedBody)); : HL(SingleQuotedBody));
@ -1806,20 +1821,23 @@ static void parse_quoted_string(ParserState *const pstate,
for (size_t i = 0; i < kv_size(shifts); i++) { for (size_t i = 0; i < kv_size(shifts); i++) {
const StringShift cur_shift = kv_A(shifts, i); const StringShift cur_shift = kv_A(shifts, i);
if (cur_shift.start > next_col) { if (cur_shift.start > next_col) {
viml_parser_highlight(pstate, shifted_pos(token.start, next_col), viml_parser_highlight(pstate, recol_pos(token.start, next_col),
cur_shift.start - next_col, cur_shift.start - next_col,
body_str); body_str);
} }
viml_parser_highlight(pstate, shifted_pos(token.start, cur_shift.start), viml_parser_highlight(pstate, recol_pos(token.start, cur_shift.start),
cur_shift.orig_len, cur_shift.orig_len,
(cur_shift.escape_not_known (cur_shift.escape_not_known
? ukn_esc_str ? ukn_esc_str
: esc_str)); : esc_str));
next_col = cur_shift.start + cur_shift.orig_len; next_col = cur_shift.start + cur_shift.orig_len;
} }
if (next_col < token.len - token.data.str.closed) { if (next_col - token.start.col < token.len - token.data.str.closed) {
viml_parser_highlight(pstate, shifted_pos(token.start, next_col), viml_parser_highlight(pstate, recol_pos(token.start, next_col),
token.len - token.data.str.closed - next_col, (token.start.col
+ token.len
- token.data.str.closed
- next_col),
body_str); body_str);
} }
} }
@ -2580,6 +2598,9 @@ viml_pexpr_parse_figure_brace_closing_error:
break; break;
} }
case kExprLexPlainIdentifier: { case kExprLexPlainIdentifier: {
const ExprVarScope scope = (cur_token.type == kExprLexInvalid
? kExprVarScopeMissing
: cur_token.data.var.scope);
if (want_node == kENodeValue || want_node == kENodeArgument) { if (want_node == kENodeValue || want_node == kENodeArgument) {
want_node = (want_node == kENodeArgument want_node = (want_node == kENodeArgument
? kENodeArgumentSeparator ? kENodeArgumentSeparator
@ -2588,9 +2609,8 @@ viml_pexpr_parse_figure_brace_closing_error:
(node_is_key (node_is_key
? kExprNodePlainKey ? kExprNodePlainKey
: kExprNodePlainIdentifier)); : kExprNodePlainIdentifier));
cur_node->data.var.scope = cur_token.data.var.scope; cur_node->data.var.scope = scope;
const size_t scope_shift = ( const size_t scope_shift = (scope == kExprVarScopeMissing ? 0 : 2);
cur_token.data.var.scope == kExprVarScopeMissing ? 0 : 2);
cur_node->data.var.ident = (pline.data + cur_token.start.col cur_node->data.var.ident = (pline.data + cur_token.start.col
+ scope_shift); + scope_shift);
cur_node->data.var.ident_len = cur_token.len - scope_shift; cur_node->data.var.ident_len = cur_token.len - scope_shift;
@ -2609,11 +2629,11 @@ viml_pexpr_parse_figure_brace_closing_error:
? HL(IdentifierKey) ? HL(IdentifierKey)
: HL(Identifier))); : HL(Identifier)));
} else { } else {
if (cur_token.data.var.scope == kExprVarScopeMissing) { if (scope == kExprVarScopeMissing) {
ADD_IDENT( ADD_IDENT(
do { do {
NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier); NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier);
cur_node->data.var.scope = cur_token.data.var.scope; cur_node->data.var.scope = scope;
cur_node->data.var.ident = pline.data + cur_token.start.col; cur_node->data.var.ident = pline.data + cur_token.start.col;
cur_node->data.var.ident_len = cur_token.len; cur_node->data.var.ident_len = cur_token.len;
want_node = kENodeOperator; want_node = kENodeOperator;

View File

@ -7006,5 +7006,144 @@ describe('Expressions parser', function()
hl('NumberPrefix', '0'), hl('NumberPrefix', '0'),
hl('Number', '0'), hl('Number', '0'),
}) })
check_parsing('"\\U\\', 0, {
-- 0123
ast = {
[[DoubleQuotedString(val="U\\"):0:0:"\U\]],
},
err = {
arg = '"\\U\\',
msg = 'E114: Missing double quote: %.*s',
},
}, {
hl('InvalidDoubleQuotedString', '"'),
hl('InvalidDoubleQuotedUnknownEscape', '\\U'),
hl('InvalidDoubleQuotedBody', '\\'),
})
check_parsing('"\\U', 0, {
-- 012
ast = {
'DoubleQuotedString(val="U"):0:0:"\\U',
},
err = {
arg = '"\\U',
msg = 'E114: Missing double quote: %.*s',
},
}, {
hl('InvalidDoubleQuotedString', '"'),
hl('InvalidDoubleQuotedUnknownEscape', '\\U'),
})
check_parsing('|"\\U\\', 2, {
-- 01234
ast = {
{
'Or:0:0:|',
children = {
'Missing:0:0:',
'DoubleQuotedString(val="U\\\\"):0:1:"\\U\\',
},
},
},
err = {
arg = '|"\\U\\',
msg = 'E15: Unexpected EOC character: %.*s',
},
}, {
hl('InvalidOr', '|'),
hl('InvalidDoubleQuotedString', '"'),
hl('InvalidDoubleQuotedUnknownEscape', '\\U'),
hl('InvalidDoubleQuotedBody', '\\'),
})
check_parsing('|"\\e"', 2, {
-- 01234
ast = {
{
'Or:0:0:|',
children = {
'Missing:0:0:',
'DoubleQuotedString(val="\\27"):0:1:"\\e"',
},
},
},
err = {
arg = '|"\\e"',
msg = 'E15: Unexpected EOC character: %.*s',
},
}, {
hl('InvalidOr', '|'),
hl('DoubleQuotedString', '"'),
hl('DoubleQuotedEscape', '\\e'),
hl('DoubleQuotedString', '"'),
})
check_parsing('|\029', 2, {
-- 01
ast = {
{
'Or:0:0:|',
children = {
'Missing:0:0:',
'PlainIdentifier(scope=0,ident=\029):0:1:\029',
},
},
},
err = {
arg = '|\029',
msg = 'E15: Unexpected EOC character: %.*s',
},
}, {
hl('InvalidOr', '|'),
hl('InvalidIdentifier', '\029'),
})
check_parsing('"\\<', 0, {
-- 012
ast = {
'DoubleQuotedString(val="<"):0:0:"\\<',
},
err = {
arg = '"\\<',
msg = 'E114: Missing double quote: %.*s',
},
}, {
hl('InvalidDoubleQuotedString', '"'),
hl('InvalidDoubleQuotedUnknownEscape', '\\<'),
})
check_parsing('"\\1', 0, {
-- 012
ast = {
'DoubleQuotedString(val="\\1"):0:0:"\\1',
},
err = {
arg = '"\\1',
msg = 'E114: Missing double quote: %.*s',
},
}, {
hl('InvalidDoubleQuotedString', '"'),
hl('InvalidDoubleQuotedEscape', '\\1'),
})
check_parsing('}l')
check_parsing(':?\000\000\000\000\000\000\000', 0, {
ast = {
{
'Colon:0:0::',
children = {
'Missing:0:0:',
{
'Ternary:0:1:?',
children = {
'Missing:0:1:',
'TernaryValue:0:1:?',
},
},
},
},
},
err = {
arg = ':?',
msg = 'E15: Colon outside of dictionary or ternary operator: %.*s',
},
}, {
hl('InvalidColon', ':'),
hl('InvalidTernary', '?'),
})
end) end)
end) end)