diff --git a/runtime/doc/vimeval.txt b/runtime/doc/vimeval.txt index c4dc1e9761..54cbaa6234 100644 --- a/runtime/doc/vimeval.txt +++ b/runtime/doc/vimeval.txt @@ -1566,9 +1566,17 @@ When using the '=' register you get the expression itself, not what it evaluates to. Use |eval()| to evaluate it. +------------------------------------------------------------------------------ nesting *expr-nesting* *E110* + (expr1) nested expression +If text follows the expression, E110 includes it, to show where the expression +should end: > + echo (0!) +< E110: Missing ')': !) ~ +Vim expects the ")" where the "!" is. + ------------------------------------------------------------------------------ environment variable *expr-env* diff --git a/scripts/linterrcodes.lua b/scripts/linterrcodes.lua index 71e27bc3e8..d36195b01c 100644 --- a/scripts/linterrcodes.lua +++ b/scripts/linterrcodes.lua @@ -10,7 +10,7 @@ local dup_allowed = { E109 = 2, E1098 = 2, - E110 = 2, + E110 = 3, E112 = 2, E114 = 2, E115 = 2, diff --git a/src/nvim/errors.h b/src/nvim/errors.h index a5a6751a9f..03ced21a04 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -130,6 +130,8 @@ EXTERN const char e_write[] INIT(= N_("E80: Error while writing")); EXTERN const char e_zerocount[] INIT(= N_("E939: Positive count required")); EXTERN const char e_usingsid[] INIT(= N_("E81: Using not in a script context")); EXTERN const char e_missingparen[] INIT(= N_("E107: Missing parentheses: %s")); +EXTERN const char e_missing_closing_paren[] INIT(= N_("E110: Missing ')'")); +EXTERN const char e_missing_closing_paren_str[] INIT(= N_("E110: Missing ')': %s")); EXTERN const char e_empty_buffer[] INIT(= N_("E749: Empty buffer")); EXTERN const char e_nobufnr[] INIT(= N_("E86: Buffer %" PRId64 " does not exist")); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 484b3202dd..301b80b453 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2755,7 +2755,12 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan if (**arg == ')') { (*arg)++; } else if (ret == OK) { - emsg(_("E110: Missing ')'")); + // Show where the expression stopped, unless at the end. + if (**arg == NUL) { + emsg(_(e_missing_closing_paren)); + } else { + semsg(_(e_missing_closing_paren_str), *arg); + } tv_clear(rettv); ret = FAIL; } diff --git a/test/old/testdir/test_expr.vim b/test/old/testdir/test_expr.vim index bd321a02e5..67067f1549 100644 --- a/test/old/testdir/test_expr.vim +++ b/test/old/testdir/test_expr.vim @@ -209,6 +209,23 @@ func Test_special_char() call CheckLegacyAndVim9Failure(['echo "\'], ['E15:', 'E1004:', 'E1004:']) endfunc +" When something else than ")" follows a nested expression, E110 shows where +" the expression ended; when nothing follows, the ")" is really missing. +func Test_missing_closing_paren() + call CheckLegacyAndVim9Failure(['echo (0!)'], "E110: Missing ')': !)") + call CheckLegacyAndVim9Failure(['echo (1 2)'], "E110: Missing ')': 2)") + call CheckLegacyAndVim9Failure(['echo (1 + 2 ]'], "E110: Missing ')': ]") + call CheckLegacyAndVim9Failure(['echo ("a" "b")'], "E110: Missing ')': \"b\")") + " in a :def function the expression may continue on the next line + call CheckLegacyAndVim9Failure(['VAR x = ((1)'], ["E110: Missing ')'", 'E1097:', "E110: Missing ')'"]) + try + echo ((1) + call assert_report('should not get here') + catch + call assert_equal("Vim(echo):E110: Missing ')'", v:exception) + endtry +endfunc + func Test_method_with_prefix() let lines =<< trim END call assert_equal(TRUE, !range(5)->empty())