runtime(c): compound literal braces are highlighted as errors
Commit:
https://github.com/vim/vim/commit/5a1124e4f59fafa0c774db956ae1e86bdd259442
Author: Bogdan Barbu <
l4b.bogd...@gmail.com>
Date: Tue Aug 4 21:11:56 2026 +0000
runtime(c): compound literal braces are highlighted as errors
Problem: Braces in C compound literals and initializers inside parentheses
and brackets are highlighted as errors.
Solution: Recognize those initializer blocks while preserving syntax recovery
for malformed parentheses.
fixes: #18709
closes: #20842
Signed-off-by: Bogdan Barbu <
l4b.bogd...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim
index 683810952..9b18c2b8a 100644
--- a/runtime/syntax/c.vim
+++ b/runtime/syntax/c.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: C
" Maintainer: The Vim Project <
https://github.com/vim/vim>
-" Last Change: 2026 Jun 01
+" Last Change: 2026 Jul 25
" Former Maintainer: Bram Moolenaar <
Br...@vim.org>
" Quit when a (custom) syntax file was already loaded
@@ -138,7 +138,7 @@ endif
" This should be before cErrInParen to avoid problems with #define ({ xxx })
if exists("c_curly_error")
syn match cCurlyError "}"
- syn region cBlock start="{" end="}" contains=ALLBUT,cBadBlock,cCurlyError,@cParenGroup,cErrInParen,cCppParen,cErrInBracket,cCppBracket,@cStringGroup,@Spell fold
+ syn region cBlock start="{" end="}" contains=ALLBUT,cBadBlock,cCurlyError,cInitBlock,@cParenGroup,cErrInParen,cCppParen,cErrInBracket,cCppBracket,@cStringGroup,@Spell fold
else
syn region cBlock start="{" end="}" transparent fold
endif
@@ -146,7 +146,7 @@ endif
" Catch errors caused by wrong parenthesis and brackets.
" Also accept <% for {, %> for }, <: for [ and :> for ] (C99)
" But avoid matching <::.
-syn cluster cParenGroup contains=cParenError,cIncluded,cSpecial,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cUserLabel,cBitField,cOctalZero,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom
+syn cluster cParenGroup contains=cParenError,cIncluded,cSpecial,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cUserLabel,cBitField,cOctalZero,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cBadParenBlock
if exists("c_no_curly_error")
if s:in_cpp_family && !exists("cpp_no_cpp11")
syn region cParen transparent start='(' end=')' contains=ALLBUT,@cParenGroup,cCppParen,@cStringGroup,@Spell
@@ -198,8 +198,15 @@ endif
if s:ft ==# 'c' || exists("cpp_no_cpp11")
syn region cBadBlock keepend start="{" end="}" contained containedin=cParen,cBracket,cBadBlock transparent fold
+ if !exists("c_no_curly_error") && !exists("c_no_bracket_error")
+ " Do not mistake an unmatched ')' followed by a block for a compound literal.
+ syn region cBadParenBlock keepend transparent start=')\_s*\ze\%({\|<%\)' matchgroup=cErrInBracket end='}\|%>' contained containedin=cBracket,cBadBlock contains=ALLBUT,cBlock,cBadBlock,cInitBlock,@cParenGroup,cErrInParen,cCppParen,cCppBracket,@cStringGroup,@Spell
+ endif
endif
+" Compound literals and initializers may appear inside parentheses or brackets.
+syn region cInitBlock transparent matchgroup=cInitBlock start='\%([)=]\_s*\)\@<=\%({\|<%\)' end='}\|%>' contained containedin=cParen,cBracket,cBadBlock contains=ALLBUT,cBlock,cCurlyError,@cParenGroup,cErrInParen,cCppParen,cErrInBracket,cCppBracket,@cStringGroup,@Spell,cInitBlock fold
+
"integer number, or floating point number without a dot and with "f".
syn case ignore
syn match cNumbers display transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctalError,cOctal
diff --git a/runtime/syntax/testdir/dumps/c_initializer_00.dump b/runtime/syntax/testdir/dumps/c_initializer_00.dump
new file mode 100644
index 000000000..426a1e958
--- /dev/null
+++ b/runtime/syntax/testdir/dumps/c_initializer_00.dump
@@ -0,0 +1,20 @@
+>/+0#0000e05#ffffff0@1| |C|o|m|p|o|u|n|d| |l|i|t|e|r|a|l|s| |a|n|d| |i|n|i|t|i|a|l|i|z|e|r|s| |i|n|s|i|d|e| |e|x|p|r|e|s@1|i|o|n|s| +0#0000000&@18
+|v+0#00e0003&|o|i|d| +0#0000000&|t|e|s|t|(|v+0#00e0003&|o|i|d|)+0#0000000&| @59
+|{| @73
+@4|i+0#00e0003&|n|t| +0#0000000&|a|[|(|s+0#00e0003&|t|r|u|c|t| +0#0000000&|f|o@1|)|{| |4+0#e000002&|2| +0#0000000&|}|.|b|a|r|]|;| @40
+@4|s+0#00e0003&|t|r|u|c|t| +0#0000000&|f|o@1| |b| |=| |(@1|s+0#00e0003&|t|r|u|c|t| +0#0000000&|f|o@1|)|{| |}|)|;| @37
+@4|_+0#af5f00255&|S|t|a|t|i|c|_|a|s@1|e|r|t|(+0#0000000&|s+0#af5f00255&|i|z|e|o|f| +0#0000000&|(|i+0#00e0003&|n|t| +0#0000000&|[|]|)|{| |1+0#e000002&|,+0#0000000&| |2+0#e000002&|,+0#0000000&| |3+0#e000002&| +0#0000000&|}| |=@1| |3+0#e000002&| +0#0000000&|*| |s+0#af5f00255&|i|z|e|o|f| +0#0000000&|(|i+0#00e0003&|n|t|)+0#0000000&|,| |"+0#e000002&@1|)+0#0000000&|;| @3
+@4|f+0#af5f00255&|o|r| +0#0000000&|(|s+0#00e0003&|t|r|u|c|t| +0#0000000&|f|o@1| |i| |=| |{| |}|;@1|)| |b+0#af5f00255&|r|e|a|k|;+0#0000000&| @37
+@4|i+0#00e0003&|n|t| +0#0000000&|c|[|(|s+0#00e0003&|t|r|u|c|t| +0#0000000&|f|o@1|)| |<|%| |4+0#e000002&|2| +0#0000000&|%|>|.|b|a|r|]|;| @37
+@4|s+0#00e0003&|t|r|u|c|t| +0#0000000&|f|o@1| |d| |=| |(@1|s+0#00e0003&|t|r|u|c|t| +0#0000000&|f|o@1|)| |<|%| |%|>|)|;| @34
+@4|f|(@1|i+0#00e0003&|n|t| +0#0000000&|[|2+0#e000002&|]+0#0000000&|[|2+0#e000002&|]+0#0000000&|)|{@1| |1+0#e000002&|,+0#0000000&| |2+0#e000002&| +0#0000000&|}|,| |{| |3+0#e000002&|,+0#0000000&| |4+0#e000002&| +0#0000000&|}@1|)|;| @34
+@4|s|y|s|c|a|l@1|(|S|Y|S|_|c|l|o|c|k|_|s|e|t@1|i|m|e|,| |C|L|O|C|K|_|R|E|A|L|T|I|M|E|,| @28
+@8|t|v| |?| |&|(@1|s+0#00e0003&|t|r|u|c|t| +0#0000000&|t|i|m|e|s|p|e|c|)|{| |.|t|v|_|s|e|c| |=| |t|v|-|>|t|v|_|s|e|c|,| @19
+@12|.|t|v|_|n|s|e|c| |=| |t|v|-|>|t|v|_|u|s|e|c| |*| |1+0#e000002&|0@2| +0#0000000&|}|)| |:| |0+0#e000002&|)+0#0000000&|;| @24
+@4|f|(@1|s+0#00e0003&|t|r|u|c|t| +0#0000000&|t|i|m|e|s|p|e|c|)| @51
+@6|<|%| |.|t|v|_|s|e|c| |=| |0+0#e000002&| +0#0000000&|%|>|)|;| @49
+@4|i+0#00e0003&|n|t| +0#0000000&|e|[|)+0#ffffff16#ff404010|{| +0#0000000#ffffff0|4+0#e000002&|2| +0#0000000&|}+0#ffffff16#ff404010|.+0#0000000#ffffff0|b|a|r|]|;| @51
+@4|f|(|a| |{+0#ffffff16#ff404010| +0#0000000#ffffff0|1+0#e000002&| +0#0000000&|}+0#ffffff16#ff404010|)+0#0000000#ffffff0|;| |f|(|a|,| |{+0#ffffff16#ff404010| +0#0000000#ffffff0|1+0#e000002&| +0#0000000&|}+0#ffffff16#ff404010|)+0#0000000#ffffff0|;| @46
+|}| @73
+|~+0#4040ff13&| @73
+| +0#0000000&@56|1|,|1| @10|A|l@1|
diff --git a/runtime/syntax/testdir/input/c_initializer.c b/runtime/syntax/testdir/input/c_initializer.c
new file mode 100644
index 000000000..a64747d46
--- /dev/null
+++ b/runtime/syntax/testdir/input/c_initializer.c
@@ -0,0 +1,18 @@
+// Compound literals and initializers inside expressions
+void test(void)
+{
+ int a[(struct foo){ 42 }.bar];
+ struct foo b = ((struct foo){ });
+ _Static_assert(sizeof (int []){ 1, 2, 3 } == 3 * sizeof (int), "");
+ for (struct foo i = { };;) break;
+ int c[(struct foo) <% 42 %>.bar];
+ struct foo d = ((struct foo) <% %>);
+ f((int [2][2]){{ 1, 2 }, { 3, 4 }});
+ syscall(SYS_clock_settime, CLOCK_REALTIME,
+ tv ? &((struct timespec){ .tv_sec = tv->tv_sec,
+ .tv_nsec = tv->tv_usec * 1000 }) : 0);
+ f((struct timespec)
+ <% .tv_sec = 0 %>);
+ int e[){ 42 }.bar];
+ f(a { 1 }); f(a, { 1 });
+}