From 1332ed9027ff764ba554ff9e931015e1be9bc9cf Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 5 Nov 2026 08:51:27 +0800 Subject: [PATCH] vim-patch:9.2.1288: E110 "missing ')'" error does not say where the problem is (#42698) Problem: An invalid nested expression such as "echo (0!)" gives "E110: Missing ')'" although the ")" is there, and does not say where the expression is not valid. Solution: When text follows the expression, add it to the E110 message, as E697 does for a List (zuloloxi). Without parentheses "echo 0!" already gives E15 with the text "!". When nothing follows the expression the ")" is really missing and the message is as before. related: vim/vim#17602 closes: vim/vim#21607 https://github.com/vim/vim/commit/d06691450ab0dc2ea72a997f8426002f66da2ac2 Co-authored-by: zuloloxi <10588750+zuloloxi@users.noreply.github.com> --- runtime/doc/vimeval.txt | 8 ++++++++ scripts/linterrcodes.lua | 2 +- src/nvim/errors.h | 2 ++ src/nvim/eval.c | 7 ++++++- test/old/testdir/test_expr.vim | 17 +++++++++++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) 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())