Linux kbuild/kconfig development
 help / color / mirror / Atom feed
* [PATCH v2 0/3] kconfig: wrap long expressions in menuconfig
@ 2026-11-10 17:35 Julian Braha
  2026-11-10 17:35 ` [PATCH v2 1/3] kconfig: print the output of expr_alloc_and() instead of printing '&&' Julian Braha
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Julian Braha @ 2026-11-10 17:35 UTC (permalink / raw)
  To: nathan, nsc
  Cc: ojeda, a.hindborg, nico, rdunlap, grahamr, jakub.git,
	linux-kbuild, linux-kernel, rust-for-linux, Julian Braha

In an offline conversation, Miguel Ojeda reported that scrolling
horizontally in menuconfig's search to read RUST's dependencies has been
slowing down debugging.

Let's wrap expressions (like in the 'Depends on', 'Selected by', or
'Range' sections) between tokens at the end of the window, such that each
line begins with the same indentation. Ensure the '!' negation stays
attached to the left side of the option symbol, and keep the value
annotation attached to the right side when wrapping.

Additionally, write a '\' at the end of wrapped lines, to visually
indicate to the reader that the line continues. 

Patch 1 cleans up the existing expression printing for 'select' and
'imply' (which ANDs the select/imply targets), to properly call 
expr_alloc_and() and print the output of that function, instead of
printing a literal ' && '. This is necessary to ensure that any '&&' at
the end of those lines will wrap properly in patch 2.

Patch 2 makes the wrapping change primarily in expr_print_gstr_helper(),
which already handles writing expression tokens to a growable string, but
now also keeps track of the wrapping info in the 'gstr' struct because
this helper is called multiple times per expression. 

Patch 3 adds a visible continuation '\' to the end of each wrapped line.
This is because some line wraps are not otherwise obvious to the reader,
for example if a line ends with a symbol instead of an operator.
(Below example uses an 80-column terminal)

Here, it would be less obvious without '\' (GPIO_TQMX86):
    Depends on: GPIOLIB [=y] && (MFD_TQMX86 [=y] || COMPILE_TEST [=y]) \  
                && HAS_IOPORT_MAP [=y]

Note that the 'help' textbox uses the same expression printer as search,
so this has the side effect of improving the formatting in 'help', too.

In the future, we should consider a larger revamp that would wrap all text
in the search window, allowing us to safely disable horizontal scrolling.
---
Changes since v1:
- add '\' at end of wrapped lines to indicate continuation (self-review)
- use expr_alloc_and() instead of printing literal ' && ' (self-review)
- properly support trailing '!' and leading spaces (sashiko / Nathan)

Link to v1:
https://lore.kernel.org/all/20261010095001.265759-1-julianbraha@gmail.com/
---
Julian Braha (3):
  kconfig: print the output of expr_alloc_and() instead of printing '&&'
  kconfig: wrap long expressions in menuconfig
  kconfig: show continuation at the end of wrapped lines

 scripts/kconfig/expr.c  | 33 ++++++++++++++++++++++++---------
 scripts/kconfig/lkc.h   |  6 +++++-
 scripts/kconfig/mconf.c |  2 +-
 scripts/kconfig/menu.c  | 24 +++++++++++++-----------
 scripts/kconfig/nconf.c |  2 +-
 scripts/kconfig/util.c  |  2 ++
 6 files changed, 46 insertions(+), 23 deletions(-)

-- 
2.56.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/3] kconfig: print the output of expr_alloc_and() instead of printing '&&'
  2026-11-10 17:35 [PATCH v2 0/3] kconfig: wrap long expressions in menuconfig Julian Braha
@ 2026-11-10 17:35 ` Julian Braha
  2026-11-10 17:35 ` [PATCH v2 2/3] kconfig: wrap long expressions in menuconfig Julian Braha
  2026-11-10 17:35 ` [PATCH v2 3/3] kconfig: show continuation at the end of wrapped lines Julian Braha
  2 siblings, 0 replies; 6+ messages in thread
From: Julian Braha @ 2026-11-10 17:35 UTC (permalink / raw)
  To: nathan, nsc
  Cc: ojeda, a.hindborg, nico, rdunlap, grahamr, jakub.git,
	linux-kbuild, linux-kernel, rust-for-linux, Julian Braha

Clean up expression printing for 'select' and 'imply' (which ANDs the
select/imply targets), to properly call expr_alloc_and() and print the
output, instead of printing a literal ' && '. 

This is necessary to ensure that any '&&' at the end of those lines can
wrap properly.

Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
 scripts/kconfig/menu.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 1a3ad09a92d2..2b2166edeb25 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -761,19 +761,16 @@ static void get_prompt_str(struct gstr *r, struct property *prop,
 static void get_symbol_props_str(struct gstr *r, struct symbol *sym,
 				 enum prop_type tok, const char *prefix)
 {
-	bool hit = false;
+	struct expr *expr = NULL;
 	struct property *prop;
 
-	for_all_properties(sym, prop, tok) {
-		if (!hit) {
-			str_append(r, prefix);
-			hit = true;
-		} else
-			str_printf(r, " && ");
-		expr_gstr_print(prop->expr, r);
-	}
-	if (hit)
+	for_all_properties(sym, prop, tok)
+		expr = expr_alloc_and(expr, prop->expr);
+	if (expr) {
+		str_append(r, prefix);
+		expr_gstr_print(expr, r);
 		str_append(r, "\n");
+	}
 }
 
 /*
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 2/3] kconfig: wrap long expressions in menuconfig
  2026-11-10 17:35 [PATCH v2 0/3] kconfig: wrap long expressions in menuconfig Julian Braha
  2026-11-10 17:35 ` [PATCH v2 1/3] kconfig: print the output of expr_alloc_and() instead of printing '&&' Julian Braha
@ 2026-11-10 17:35 ` Julian Braha
  2026-11-11 17:35   ` sashiko-bot
  2026-11-10 17:35 ` [PATCH v2 3/3] kconfig: show continuation at the end of wrapped lines Julian Braha
  2 siblings, 1 reply; 6+ messages in thread
From: Julian Braha @ 2026-11-10 17:35 UTC (permalink / raw)
  To: nathan, nsc
  Cc: ojeda, a.hindborg, nico, rdunlap, grahamr, jakub.git,
	linux-kbuild, linux-kernel, rust-for-linux, Julian Braha

Horizontally scrolling through long expressions in menuconfig search is
overly time-consuming.

Let's wrap expressions (like in the 'Depends on', 'Selected by', or
'Range' sections) between tokens at the end of the window, such that each
line begins with the same indentation. Ensure the '!' negation stays
attached to the left side of the option symbol, and keep the value
annotation attached to the right side when wrapping.

Note that the 'help' textbox uses the same expression printer as search,
so this has the side effect of improving the formatting in 'help', too.

In the future, we should consider a larger revamp that would wrap all text
in the search window, allowing us to safely disable horizontal scrolling.

Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
Changes since v1:
- properly support trailing '!' and leading spaces (sashiko / Nathan)

Link to v1:
https://lore.kernel.org/all/20261010095001.265759-1-julianbraha@gmail.com/
 scripts/kconfig/expr.c  | 28 ++++++++++++++++++++--------
 scripts/kconfig/lkc.h   |  6 +++++-
 scripts/kconfig/mconf.c |  2 +-
 scripts/kconfig/menu.c  |  7 ++++++-
 scripts/kconfig/nconf.c |  2 +-
 scripts/kconfig/util.c  |  2 ++
 6 files changed, 35 insertions(+), 12 deletions(-)

diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index 2b91d16bf14f..9284ccb20259 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -1155,30 +1155,40 @@ static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *s
 	if (sym)
 		sym_str = sym_get_string_value(sym);
 
-	if (gs->max_width) {
+	if (gs->max_width && !strchr(str, '\n')) {
 		unsigned extra_length = strlen(str);
 		const char *last_cr = strrchr(gs->s, '\n');
 		unsigned last_line_length;
+		unsigned int prefix_len = strlen(gs->s) - gs->wrap_pos;
 
-		if (sym_str)
+		/* Ensure the value annotation stays attached to the symbol on wrap */
+		if (sym_str && sym->type != S_UNKNOWN)
 			extra_length += 4 + strlen(sym_str);
 
-		if (!last_cr)
-			last_cr = gs->s;
+		last_line_length = strlen(last_cr ? last_cr + 1 : gs->s);
 
-		last_line_length = strlen(gs->s) - (last_cr - gs->s);
-
-		if ((last_line_length + extra_length) > gs->max_width)
-			str_append(gs, "\\\n");
+		if (last_line_length > gs->indent + prefix_len &&
+		    last_line_length + extra_length > gs->max_width) {
+			gs->s[gs->wrap_pos] = '\0';
+			str_printf(gs, "\n%*s", gs->indent, "");
+			while (prefix_len--)
+				str_append(gs, "!");
+			if (!sym)
+				str += strspn(str, " ");
+		}
 	}
 
 	str_append(gs, str);
 	if (sym && sym->type != S_UNKNOWN)
 		str_printf(gs, " [=%s]", sym_str);
+	/* Ensure '!' stays attached to the symbol on wrap */
+	if (sym || strcmp(str, "!"))
+		gs->wrap_pos = strlen(gs->s);
 }
 
 void expr_gstr_print(const struct expr *e, struct gstr *gs)
 {
+	gs->wrap_pos = strlen(gs->s);
 	expr_print(e, expr_print_gstr_helper, gs, E_NONE);
 }
 
@@ -1209,5 +1219,7 @@ static void expr_print_revdep(struct expr *e,
 void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
 			    tristate pr_type, const char *title)
 {
+	gs->indent = strlen("  - ");
+	gs->wrap_pos = strlen(gs->s);
 	expr_print_revdep(e, expr_print_gstr_helper, gs, pr_type, &title);
 }
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index bbc99f75b416..a1d3e366fb76 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -65,6 +65,9 @@ struct gstr {
 	* wrapped not to exceed the max_width value
 	*/
 	int max_width;
+	/* Indentation of wrapped expression lines. */
+	int indent;
+	size_t wrap_pos;
 };
 struct gstr str_new(void);
 void str_free(struct gstr *gs);
@@ -101,7 +104,8 @@ const char *menu_get_prompt(const struct menu *menu);
 struct menu *menu_get_parent_menu(struct menu *menu);
 struct menu *menu_get_menu_or_parent_menu(struct menu *menu);
 int get_jump_key_char(void);
-struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head);
+struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head,
+			      int max_width);
 void menu_get_ext_help(struct menu *menu, struct gstr *help);
 void menu_dump(void);
 
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index ebfc5bd09c1c..ba258a032094 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -433,7 +433,7 @@ static void search_conf(void)
 		struct jump_key *pos, *tmp;
 
 		jump_key_char = 0;
-		res = get_relations_str(sym_arr, &head);
+		res = get_relations_str(sym_arr, &head, getmaxx(stdscr) - 10);
 		set_subtitle();
 
 		res_title = str_new();
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 2b2166edeb25..be5a19c8e0ed 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -689,6 +689,7 @@ static void get_dep_str(struct gstr *r, const struct expr *expr,
 {
 	if (!expr_is_yes(expr)) {
 		str_append(r, prefix);
+		r->indent = strlen(prefix);
 		expr_gstr_print(expr, r);
 		str_append(r, "\n");
 	}
@@ -768,6 +769,7 @@ static void get_symbol_props_str(struct gstr *r, struct symbol *sym,
 		expr = expr_alloc_and(expr, prop->expr);
 	if (expr) {
 		str_append(r, prefix);
+		r->indent = strlen(prefix);
 		expr_gstr_print(expr, r);
 		str_append(r, "\n");
 	}
@@ -790,6 +792,7 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym,
 			prop = sym_get_range_prop(sym);
 			if (prop) {
 				str_printf(r, "Range : ");
+				r->indent = strlen("Range : ");
 				expr_gstr_print(prop->expr, r);
 				str_append(r, "\n");
 			}
@@ -828,12 +831,14 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym,
 	str_append(r, "\n\n");
 }
 
-struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head)
+struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head,
+			      int max_width)
 {
 	struct symbol *sym;
 	struct gstr res = str_new();
 	int i;
 
+	res.max_width = max_width;
 	for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
 		get_symbol_str(&res, sym, head);
 	if (!i)
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index 4c9c4ee7c3a7..83c13ff108d6 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -743,7 +743,7 @@ static void search_conf(void)
 			.target = NULL,
 		};
 		jump_key_char = 0;
-		res = get_relations_str(sym_arr, &head);
+		res = get_relations_str(sym_arr, &head, 0);
 
 		res_title = str_new();
 		str_printf(&res_title, "Search Results (%d)", sym_cnt);
diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
index 0809aa061b6a..5b80393048cb 100644
--- a/scripts/kconfig/util.c
+++ b/scripts/kconfig/util.c
@@ -77,6 +77,8 @@ struct gstr str_new(void)
 	gs.s = xmalloc(sizeof(char) * 64);
 	gs.len = 64;
 	gs.max_width = 0;
+	gs.indent = 0;
+	gs.wrap_pos = 0;
 	strcpy(gs.s, "\0");
 	return gs;
 }
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 3/3] kconfig: show continuation at the end of wrapped lines
  2026-11-10 17:35 [PATCH v2 0/3] kconfig: wrap long expressions in menuconfig Julian Braha
  2026-11-10 17:35 ` [PATCH v2 1/3] kconfig: print the output of expr_alloc_and() instead of printing '&&' Julian Braha
  2026-11-10 17:35 ` [PATCH v2 2/3] kconfig: wrap long expressions in menuconfig Julian Braha
@ 2026-11-10 17:35 ` Julian Braha
  2 siblings, 0 replies; 6+ messages in thread
From: Julian Braha @ 2026-11-10 17:35 UTC (permalink / raw)
  To: nathan, nsc
  Cc: ojeda, a.hindborg, nico, rdunlap, grahamr, jakub.git,
	linux-kbuild, linux-kernel, rust-for-linux, Julian Braha

Add a visible continuation '\' to the end of each wrapped line.
This is because some line wraps are not otherwise obvious to the reader,
for example if a line ends with a symbol name instead of an operator.
(Below example uses an 80-column terminal)

Here, it would be less obvious without '\' (GPIO_TQMX86):
    Depends on: GPIOLIB [=y] && (MFD_TQMX86 [=y] || COMPILE_TEST [=y]) \  
                && HAS_IOPORT_MAP [=y]

Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
 scripts/kconfig/expr.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index 0b3f2d7bc809..a539b617b62f 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -1167,12 +1167,15 @@ static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *s
 		if (sym_str && sym->type != S_UNKNOWN)
 			extra_length += 4 + strlen(sym_str);
 
+		extra_length += strlen(" \\");
 		last_line_length = strlen(last_cr ? last_cr + 1 : gs->s);
 
 		if (last_line_length > gs->indent + prefix_len &&
 		    last_line_length + extra_length > gs->max_width) {
 			gs->s[gs->wrap_pos] = '\0';
-			str_printf(gs, "\n%*s", gs->indent, "");
+			if (gs->s[gs->wrap_pos - 1] != ' ')
+				str_append(gs, " ");
+			str_printf(gs, "\\\n%*s", gs->indent, "");
 			while (prefix_len--)
 				str_append(gs, "!");
 			if (!sym)
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 2/3] kconfig: wrap long expressions in menuconfig
  2026-11-10 17:35 ` [PATCH v2 2/3] kconfig: wrap long expressions in menuconfig Julian Braha
@ 2026-11-11 17:35   ` sashiko-bot
  2026-11-11 18:07     ` Julian Braha
  0 siblings, 1 reply; 6+ messages in thread
From: sashiko-bot @ 2026-11-11 17:35 UTC (permalink / raw)
  To: Julian Braha, nathan, nsc
  Cc: ojeda, a.hindborg, nico, rdunlap, grahamr, jakub.git,
	linux-kbuild, gary

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Low] kconfig: broken '(' and operator wrapping in
  expr_print_gstr_helper()
--

commit 89202a55a9cf081fc680fbfa051ebe8d0cfa7113
Author: Julian Braha <julianbraha@gmail.com>

kconfig: wrap long expressions in menuconfig

This commit wraps long Kconfig expressions across lines with matching
indentation in menuconfig search and help text, keeping '!' prefixes and
value annotations attached to their symbols.

> diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
> index 2b91d16bf14fb..9284ccb20259c 100644
> --- a/scripts/kconfig/expr.c
> +++ b/scripts/kconfig/expr.c
> @@ -1155,30 +1155,40 @@ static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *s
>  	if (sym)
>  		sym_str = sym_get_string_value(sym);
>  
> -	if (gs->max_width) {
> +	if (gs->max_width && !strchr(str, '\n')) {
>  		unsigned extra_length = strlen(str);
>  		const char *last_cr = strrchr(gs->s, '\n');
>  		unsigned last_line_length;
> +		unsigned int prefix_len = strlen(gs->s) - gs->wrap_pos;
>  
> -		if (sym_str)
> +		/* Ensure the value annotation stays attached to the symbol on wrap */
> +		if (sym_str && sym->type != S_UNKNOWN)
>  			extra_length += 4 + strlen(sym_str);
>  
> -		if (!last_cr)
> -			last_cr = gs->s;
> +		last_line_length = strlen(last_cr ? last_cr + 1 : gs->s);
>  
> -		last_line_length = strlen(gs->s) - (last_cr - gs->s);
> -
> -		if ((last_line_length + extra_length) > gs->max_width)
> -			str_append(gs, "\\\n");
> +		if (last_line_length > gs->indent + prefix_len &&
> +		    last_line_length + extra_length > gs->max_width) {
> +			gs->s[gs->wrap_pos] = '\0';
> +			str_printf(gs, "\n%*s", gs->indent, "");
> +			while (prefix_len--)
> +				str_append(gs, "!");
> +			if (!sym)
> +				str += strspn(str, " ");
> +		}
>  	}
>  
>  	str_append(gs, str);
>  	if (sym && sym->type != S_UNKNOWN)
>  		str_printf(gs, " [=%s]", sym_str);
> +	/* Ensure '!' stays attached to the symbol on wrap */
> +	if (sym || strcmp(str, "!"))
> +		gs->wrap_pos = strlen(gs->s);
>  }

[Severity: Low]
Does expr_print_gstr_helper() mishandle wrapping around "(" and wrapped
operators?

When expr_print() formats a negated parenthesized expression such as
"!(A || B)", it emits "!" and then "(" before the first symbol.  Because
strcmp("(", "!") is non-zero, printing "(" updates gs->wrap_pos to point
after "!(" and resets prefix_len to 0.  If the first symbol plus its
"[=...]" annotation then exceeds gs->max_width, it wraps after "(",
leaving "!(" (or "(") dangling at the end of the previous line.

In addition, the guard "last_line_length > gs->indent + prefix_len"
assumes only indentation and "!" can precede the first symbol on a line.
After " && " or " || " wraps onto a new line (or after "(" or "[" is
printed at the start of a line), gs->wrap_pos is updated past that token,
making last_line_length greater than gs->indent + prefix_len before any
symbol is printed on the line.  If the following symbol and its value
annotation exceed gs->max_width, expr_print_gstr_helper() wraps a second
time and leaves "&& ", "|| ", "(" or "[" stranded on a line by itself.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261110173526.1548434-1-julianbraha@gmail.com?part=2

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 2/3] kconfig: wrap long expressions in menuconfig
  2026-11-11 17:35   ` sashiko-bot
@ 2026-11-11 18:07     ` Julian Braha
  0 siblings, 0 replies; 6+ messages in thread
From: Julian Braha @ 2026-11-11 18:07 UTC (permalink / raw)
  To: sashiko-reviews, nathan, nsc
  Cc: ojeda, a.hindborg, nico, rdunlap, grahamr, jakub.git,
	linux-kbuild, gary


[-- Attachment #1.1: Type: text/plain, Size: 3688 bytes --]

On 11/11/26 17:35, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue to consider:
> - [Low] kconfig: broken '(' and operator wrapping in
>   expr_print_gstr_helper()
> --
> 
> commit 89202a55a9cf081fc680fbfa051ebe8d0cfa7113
> Author: Julian Braha <julianbraha@gmail.com>
> 
> kconfig: wrap long expressions in menuconfig
> 
> This commit wraps long Kconfig expressions across lines with matching
> indentation in menuconfig search and help text, keeping '!' prefixes and
> value annotations attached to their symbols.
> 
>> diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
>> index 2b91d16bf14fb..9284ccb20259c 100644
>> --- a/scripts/kconfig/expr.c
>> +++ b/scripts/kconfig/expr.c
>> @@ -1155,30 +1155,40 @@ static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *s
>>  	if (sym)
>>  		sym_str = sym_get_string_value(sym);
>>  
>> -	if (gs->max_width) {
>> +	if (gs->max_width && !strchr(str, '\n')) {
>>  		unsigned extra_length = strlen(str);
>>  		const char *last_cr = strrchr(gs->s, '\n');
>>  		unsigned last_line_length;
>> +		unsigned int prefix_len = strlen(gs->s) - gs->wrap_pos;
>>  
>> -		if (sym_str)
>> +		/* Ensure the value annotation stays attached to the symbol on wrap */
>> +		if (sym_str && sym->type != S_UNKNOWN)
>>  			extra_length += 4 + strlen(sym_str);
>>  
>> -		if (!last_cr)
>> -			last_cr = gs->s;
>> +		last_line_length = strlen(last_cr ? last_cr + 1 : gs->s);
>>  
>> -		last_line_length = strlen(gs->s) - (last_cr - gs->s);
>> -
>> -		if ((last_line_length + extra_length) > gs->max_width)
>> -			str_append(gs, "\\\n");
>> +		if (last_line_length > gs->indent + prefix_len &&
>> +		    last_line_length + extra_length > gs->max_width) {
>> +			gs->s[gs->wrap_pos] = '\0';
>> +			str_printf(gs, "\n%*s", gs->indent, "");
>> +			while (prefix_len--)
>> +				str_append(gs, "!");
>> +			if (!sym)
>> +				str += strspn(str, " ");
>> +		}
>>  	}
>>  
>>  	str_append(gs, str);
>>  	if (sym && sym->type != S_UNKNOWN)
>>  		str_printf(gs, " [=%s]", sym_str);
>> +	/* Ensure '!' stays attached to the symbol on wrap */
>> +	if (sym || strcmp(str, "!"))
>> +		gs->wrap_pos = strlen(gs->s);
>>  }
> [Severity: Low]
> Does expr_print_gstr_helper() mishandle wrapping around "(" and wrapped
> operators?
> 
> When expr_print() formats a negated parenthesized expression such as
> "!(A || B)", it emits "!" and then "(" before the first symbol.  Because
> strcmp("(", "!") is non-zero, printing "(" updates gs->wrap_pos to point
> after "!(" and resets prefix_len to 0.  If the first symbol plus its
> "[=...]" annotation then exceeds gs->max_width, it wraps after "(",
> leaving "!(" (or "(") dangling at the end of the previous line.
> 
> In addition, the guard "last_line_length > gs->indent + prefix_len"
> assumes only indentation and "!" can precede the first symbol on a line.
> After " && " or " || " wraps onto a new line (or after "(" or "[" is
> printed at the start of a line), gs->wrap_pos is updated past that token,
> making last_line_length greater than gs->indent + prefix_len before any
> symbol is printed on the line.  If the following symbol and its value
> annotation exceed gs->max_width, expr_print_gstr_helper() wraps a second
> time and leaves "&& ", "|| ", "(" or "[" stranded on a line by itself.
> 
> -- Sashiko AI review · https://sashiko.dev/#/
> patchset/20261110173526.1548434-1-julianbraha@gmail.com?part=2
> 

Sashiko is hallucinating this time.

I mean, what it's saying is correct, but this is intended behavior not a
bug.

- Julian Braha

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 236 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-11 18:07 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-10 17:35 [PATCH v2 0/3] kconfig: wrap long expressions in menuconfig Julian Braha
2026-11-10 17:35 ` [PATCH v2 1/3] kconfig: print the output of expr_alloc_and() instead of printing '&&' Julian Braha
2026-11-10 17:35 ` [PATCH v2 2/3] kconfig: wrap long expressions in menuconfig Julian Braha
2026-11-11 17:35   ` sashiko-bot
2026-11-11 18:07     ` Julian Braha
2026-11-10 17:35 ` [PATCH v2 3/3] kconfig: show continuation at the end of wrapped lines Julian Braha

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox