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
// NVimInvalidDoubleQuotedBody -> NVimInvalidString
// NVimInvalidDoubleQuotedEscape -> NVimInvalidStringSpecial
// NVimInvalidDoubleQuotedUnknownEscape -> NVimInvalid
// NVimInvalidDoubleQuotedUnknownEscape -> NVimInvalidDoubleQuotedEscape
//
// NVimFigureBrace -> 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
///
/// 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] 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 };
}
/// 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
#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);
int nr = 0;
p++;
while (n-- && ascii_isxdigit(p[1])) {
while (p + 1 < e && n-- && ascii_isxdigit(p[1])) {
p++;
nr = (nr << 4) + hex2nr(*p);
}
@ -1659,7 +1674,7 @@ static void parse_quoted_string(ParserState *const pstate,
if (*p >= '0' && *p <= '7') {
size--;
p++;
if (*p >= '0' && *p <= '7') {
if (p < e && *p >= '0' && *p <= '7') {
size--;
p++;
}
@ -1715,7 +1730,7 @@ static void parse_quoted_string(ParserState *const pstate,
// Hexadecimal or unicode.
case 'X': case 'x': case 'u': case 'U': {
if (ascii_isxdigit(p[1])) {
if (p + 1 < e && ascii_isxdigit(p[1])) {
size_t n;
int nr;
bool is_hex = (*p == 'x' || *p == 'X');
@ -1728,7 +1743,7 @@ static void parse_quoted_string(ParserState *const pstate,
n = 8;
}
nr = 0;
while (n-- && ascii_isxdigit(p[1])) {
while (p + 1 < e && n-- && ascii_isxdigit(p[1])) {
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 '7': {
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');
if (*p >= '0' && *p <= '7') {
if (p < e && *p >= '0' && *p <= '7') {
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 printf format str
// 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
? HL(DoubleQuotedBody)
: HL(SingleQuotedBody));
@ -1806,20 +1821,23 @@ static void parse_quoted_string(ParserState *const pstate,
for (size_t i = 0; i < kv_size(shifts); i++) {
const StringShift cur_shift = kv_A(shifts, i);
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,
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.escape_not_known
? ukn_esc_str
: esc_str));
next_col = cur_shift.start + cur_shift.orig_len;
}
if (next_col < token.len - token.data.str.closed) {
viml_parser_highlight(pstate, shifted_pos(token.start, next_col),
token.len - token.data.str.closed - next_col,
if (next_col - token.start.col < token.len - token.data.str.closed) {
viml_parser_highlight(pstate, recol_pos(token.start, next_col),
(token.start.col
+ token.len
- token.data.str.closed
- next_col),
body_str);
}
}
@ -2580,6 +2598,9 @@ viml_pexpr_parse_figure_brace_closing_error:
break;
}
case kExprLexPlainIdentifier: {
const ExprVarScope scope = (cur_token.type == kExprLexInvalid
? kExprVarScopeMissing
: cur_token.data.var.scope);
if (want_node == kENodeValue || want_node == kENodeArgument) {
want_node = (want_node == kENodeArgument
? kENodeArgumentSeparator
@ -2588,9 +2609,8 @@ viml_pexpr_parse_figure_brace_closing_error:
(node_is_key
? kExprNodePlainKey
: kExprNodePlainIdentifier));
cur_node->data.var.scope = cur_token.data.var.scope;
const size_t scope_shift = (
cur_token.data.var.scope == kExprVarScopeMissing ? 0 : 2);
cur_node->data.var.scope = scope;
const size_t scope_shift = (scope == kExprVarScopeMissing ? 0 : 2);
cur_node->data.var.ident = (pline.data + cur_token.start.col
+ 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(Identifier)));
} else {
if (cur_token.data.var.scope == kExprVarScopeMissing) {
if (scope == kExprVarScopeMissing) {
ADD_IDENT(
do {
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_len = cur_token.len;
want_node = kENodeOperator;

View File

@ -7006,5 +7006,144 @@ describe('Expressions parser', function()
hl('NumberPrefix', '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)