]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
kconfig: fix submenu rendering of negative dependencies
authorJulian Braha <julianbraha@gmail.com>
Sat, 1 Aug 2026 14:52:38 +0000 (15:52 +0100)
committerNicolas Schier <nsc@kernel.org>
Fri, 14 Aug 2026 19:33:20 +0000 (21:33 +0200)
The Kconfig frontend should render options that depend on a
previous option in the submenu of that previous option. But
currently, this breaks for negative dependencies. For example,
option FOO may be rendered in the submenu of option BAR, despite
FOO actually depending on !BAR.

Let's fix this ironic rendering by modifying Kconfig to explicitly
check negative dependencies.

I've only tested locally on x86, but as far as I can tell, this only
changes how 2 options are rendered in the menu:
1. NTFS3_FS, no longer in the NTFS_FS submenu, and
2. MTD_BLOCK_RO, no longer in the MTD_BLOCK submenu.

Tested-by: Nathan Chancellor <nathan@kernel.org>
Reported-by: Xi Ruoyao <xry111@xry111.site>
Closes: https://lore.kernel.org/all/cbe95c15d2760f6fce8eaf207c969ce8fd3703aa.camel@xry111.site/
Assisted-by: Claude:claude-4.8-opus
Signed-off-by: Julian Braha <julianbraha@gmail.com>
Link: https://patch.msgid.link/20260801145238.2140291-1-julianbraha@gmail.com
Reviewed-by: Nicolas Schier <nsc@kernel.org>
Tested-by: Nicolas Schier <nsc@kernel.org>
Signed-off-by: Nicolas Schier <nsc@kernel.org>
scripts/kconfig/expr.c
scripts/kconfig/expr.h
scripts/kconfig/menu.c

index 16f92c4a775a13f053c636598df70702b8fe7c4d..2b91d16bf14fb78cba386053b9cfd2a60b843f03 100644 (file)
@@ -738,6 +738,39 @@ bool expr_contains_symbol(struct expr *dep, struct symbol *sym)
        return false;
 }
 
+/*
+ * Check if the expression references 'sym' in a way that is satisfiable
+ * with 'sym' disabled, e.g.'sym!=y'.
+ *
+ * Expects that expr_transform() was already called on 'expr'.
+ */
+bool expr_contains_symbol_negated(struct expr *dep, struct symbol *sym)
+{
+       if (!dep)
+               return false;
+
+       switch (dep->type) {
+       case E_AND:
+       case E_OR:
+               return expr_contains_symbol_negated(dep->left.expr, sym) ||
+                      expr_contains_symbol_negated(dep->right.expr, sym);
+       case E_NOT:
+               return dep->left.expr->type == E_SYMBOL &&
+                      dep->left.expr->left.sym == sym;
+       case E_EQUAL:
+               /* sym=n */
+               return dep->left.sym == sym && dep->right.sym == &symbol_no;
+       case E_UNEQUAL:
+               /* sym!=y, sym!=m */
+               return dep->left.sym == sym &&
+                      (dep->right.sym == &symbol_yes ||
+                       dep->right.sym == &symbol_mod);
+       default:
+               break;
+       }
+       return false;
+}
+
 bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
 {
        if (!dep)
index fa3823a97d726a91ad3b0193d8b940372155d4ce..b580f9fa0f29c52a66d372c3dafb601594772092 100644 (file)
@@ -307,6 +307,7 @@ tristate expr_calc_value(struct expr *e);
 struct expr *expr_eliminate_dups(struct expr *e);
 struct expr *expr_transform(struct expr *e);
 bool expr_contains_symbol(struct expr *dep, struct symbol *sym);
+bool expr_contains_symbol_negated(struct expr *dep, struct symbol *sym);
 bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
 struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
 
index b2d8d4e11e07c02d7bb5c4ed7713fb4334d9dbf5..9c079e92a9ed62d7a00d8090f7732ea3b851c5a9 100644 (file)
@@ -428,9 +428,19 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
                        if (!expr_contains_symbol(dep, sym))
                                /* No dependency, quit */
                                break;
+                       /*
+                        * Note that it's actually possible to depend on both
+                        * 'SYM!=y' and 'SYM=y', so we need to first check if
+                        * it's a positive dependency before checking if it's
+                        * a negative dependency. See example:
+                        * 'SFC && MTD && !(SFC=y && MTD=m)'
+                        */
                        if (expr_depends_symbol(dep, sym))
                                /* Absolute dependency, put in submenu */
                                goto next;
+                       if (expr_contains_symbol_negated(dep, sym))
+                               /* Negative dependency, quit */
+                               break;
 
                        /*
                         * Also consider it a dependency on sym if our