patch 9.2.0998: Reject non-string-literal arguments to STRLEN_LITERAL
Commit:
https://github.com/vim/vim/commit/93112a775124889ce3ea8b93f87a2831ca0d96bf
Author: Shane Harper <
sh...@shaneharper.net>
Date: Sun Aug 23 19:01:15 2026 +0000
patch 9.2.0998: Reject non-string-literal arguments to STRLEN_LITERAL
Problem: STRLEN_LITERAL silently expands to an expression that doesn't
determine a string's length if it's given a non-string-literal
token.
Solution: Modify STRLEN_LITERAL so that a non-string-literal argument
produces a compile error (Shane Harper).
Regarding character arrays:
A compilation error will now be generated for the following:
char s[64] = "12345";
int l = STRLEN_LITERAL(s); // previously l was assigned 63 (not 5).
Had s been defined as:
char s[] = "12345";
the original definition of STRLEN_LITERAL would have correctly
determined the string length to be 5; however, the macro cannot
distinguish a padded array like the first from an exactly-sized one like
the second, so both are rejected.
Supported by AI (Claude Sonnet 5).
closes: #21122
Signed-off-by: Shane Harper <
sh...@shaneharper.net>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/ascii.h b/src/ascii.h
index ec839704e..5941b1b09 100644
--- a/src/ascii.h
+++ b/src/ascii.h
@@ -87,6 +87,6 @@
# define PATHSEP psepc
# define PATHSEPSTR pseps
#else
-# define PATHSEP '/'
+# define PATHSEP ((char_u)'/')
# define PATHSEPSTR "/"
#endif
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 98ce6259f..45a9a7dd4 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -4135,7 +4135,7 @@ expand_shellcmd(
// Do not match directories inside a $PATH item.
flags &= ~EW_DIR;
- seplen = !after_pathsep(s, e) ? STRLEN_LITERAL(PATHSEPSTR) : 0;
+ seplen = !after_pathsep(s, e) ? sizeof(PATHSEP) : 0;
}
// Make sure that the pathed pattern (ie the path and pattern concatenated
diff --git a/src/fileio.c b/src/fileio.c
index 255e1b33d..8326be11c 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -5424,7 +5424,7 @@ vim_settempdir(char_u *tempdir)
if (!after_pathsep(buf, buf + buflen))
{
STRCPY(buf + buflen, PATHSEPSTR);
- buflen += STRLEN_LITERAL(PATHSEPSTR);
+ buflen += sizeof(PATHSEP);
}
vim_tempdir = vim_strnsave(buf, buflen);
# if defined(UNIX) && defined(HAVE_FLOCK) && defined(HAVE_DIRFD)
@@ -5501,7 +5501,7 @@ vim_tempname(
if (!after_pathsep(itmp, itmp + itmplen))
{
STRCPY(itmp + itmplen, PATHSEPSTR);
- itmplen += STRLEN_LITERAL(PATHSEPSTR);
+ itmplen += sizeof(PATHSEP);
}
# ifdef HAVE_MKDTEMP
diff --git a/src/filepath.c b/src/filepath.c
index 388ce7af1..32750eead 100644
--- a/src/filepath.c
+++ b/src/filepath.c
@@ -3200,7 +3200,7 @@ vim_fnamencmp(char_u *x, char_u *y, size_t len)
char_u *
concat_fnames(char_u *fname1, size_t fname1len, char_u *fname2, size_t fname2len, int sep, string_T *ret)
{
- ret->string = alloc(fname1len + (sep ? STRLEN_LITERAL(PATHSEPSTR) : 0) + fname2len + 1);
+ ret->string = alloc(fname1len + (sep ? sizeof(PATHSEP) : 0) + fname2len + 1);
if (ret->string == NULL)
ret->length = 0;
else
@@ -3210,7 +3210,7 @@ concat_fnames(char_u *fname1, size_t fname1len, char_u *fname2, size_t fname2len
if (sep && *ret->string != NUL && !after_pathsep(ret->string, ret->string + ret->length))
{
STRCPY(ret->string + ret->length, PATHSEPSTR);
- ret->length += STRLEN_LITERAL(PATHSEPSTR);
+ ret->length += sizeof(PATHSEP);
}
STRCPY(ret->string + ret->length, fname2);
ret->length += fname2len;
diff --git a/src/findfile.c b/src/findfile.c
index af9523dcd..25ffd115d 100644
--- a/src/findfile.c
+++ b/src/findfile.c
@@ -2678,7 +2678,7 @@ uniquefy_paths(
continue;
}
- rel_pathsize = 1 + STRLEN_LITERAL(PATHSEPSTR) + STRLEN(short_name) + 1;
+ rel_pathsize = 1 + sizeof(PATHSEP) + STRLEN(short_name) + 1;
rel_path = alloc(rel_pathsize);
if (rel_path == NULL)
goto theend;
diff --git a/src/help.c b/src/help.c
index c8155054d..be4a7a038 100644
--- a/src/help.c
+++ b/src/help.c
@@ -795,7 +795,7 @@ fix_help_buffer(void)
if (*NameBuff != NUL && !after_pathsep(NameBuff, NameBuff + NameBufflen))
{
STRCPY(NameBuff + NameBufflen, PATHSEPSTR);
- NameBufflen += STRLEN_LITERAL(PATHSEPSTR);
+ NameBufflen += sizeof(PATHSEP);
}
#ifdef FEAT_MULTI_LANG
STRCPY(NameBuff + NameBufflen, "doc/*.??[tx]");
diff --git a/src/os_unix.c b/src/os_unix.c
index 5a6ccacc9..ebdc8d04a 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -2824,7 +2824,7 @@ mch_FullName(
&& STRCMP(fname, ".") != 0)
{
STRCPY(buf + buflen, PATHSEPSTR);
- buflen += STRLEN_LITERAL(PATHSEPSTR);
+ buflen += sizeof(PATHSEP);
}
#endif
}
diff --git a/src/scriptfile.c b/src/scriptfile.c
index c5f807562..302fae91b 100644
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -667,7 +667,7 @@ do_in_path(
&& !after_pathsep(buf.string, buf.string + buf.length))
{
STRCPY(buf.string + buf.length, PATHSEPSTR);
- buf.length += STRLEN_LITERAL(PATHSEPSTR);
+ buf.length += sizeof(PATHSEP);
}
STRCPY(buf.string + buf.length, prefix);
buf.length += prefixlen;
diff --git a/src/structs.h b/src/structs.h
index 984acc1d9..2953ccb02 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -5472,8 +5472,11 @@ typedef struct {
#endif
} spellvars_T;
-// Return the length of a string literal
-#define STRLEN_LITERAL(s) (sizeof(s) - 1)
+// Return the length of a string literal.
+// This macro only computes a string's length for a string-literal token; for
+// anything else, including a char*, compilation will fail (note "" following
+// s).
+#define STRLEN_LITERAL(s) (sizeof(s "") - 1)
// Store a key/value (string) pair
typedef struct
diff --git a/src/version.c b/src/version.c
index 3c26d9443..d33f63fed 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 998,
/**/
997,
/**/