Linux kbuild/kconfig development
 help / color / mirror / Atom feed
* [PATCH] kconfig: warn on dead default
@ 2026-06-06 14:00 Julian Braha
  2026-06-06 14:23 ` Sam Ravnborg
  0 siblings, 1 reply; 4+ messages in thread
From: Julian Braha @ 2026-06-06 14:00 UTC (permalink / raw)
  To: nathan, nsc
  Cc: rdunlap, masahiroy, grahamr, nico, linux-kernel, linux-kbuild,
	Julian Braha

The dead default check was originally introduced with kconfirm:
https://lore.kernel.org/all/6ec4df6d-1445-48ca-8f54-1d1a83c4716d@gmail.com/

While I'm still working on that tool, it's not yet ready for inclusion
into the tree. I am currently waiting for common distro packagers to
package the parsing library before submitting the next RFC iteration.

However, the dead default check is more impactful than the other checks:
all 4 dead defaults that were detected should not have been dead and could
cause misconfiguration bugs. But fortunately, these were just for kunit
tests. The 3 patches to fix them have all since been merged:
commit aef656a0e6c0 ("powerpc: fix dead default for GUEST_STATE_BUFFER_TEST")
commit 30cc5e2ad826 ("s390/Kconfig: Cleanup defaults for selftests")
commit df75430515c3 ("drm: fix dead default for DRM_TTM_KUNIT_TEST")

We can actually check for dead defaults while evaluating Kconfig, which
should be even more effective at preventing future instances than keeping
it in a static checker.

Note that this patch will only trigger a warning when the default values
are different, in other words, pure duplicate defaults won't cause a
warning, as they are simply redundant.

Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
 scripts/kconfig/menu.c                        | 22 +++++++++-
 .../kconfig/tests/warn_dead_default/Kconfig   | 40 +++++++++++++++++++
 .../tests/warn_dead_default/__init__.py       |  8 ++++
 .../tests/warn_dead_default/expected_stderr   |  4 ++
 4 files changed, 73 insertions(+), 1 deletion(-)
 create mode 100644 scripts/kconfig/tests/warn_dead_default/Kconfig
 create mode 100644 scripts/kconfig/tests/warn_dead_default/__init__.py
 create mode 100644 scripts/kconfig/tests/warn_dead_default/expected_stderr

diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index b2d8d4e11e07..8c280292f9cd 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -242,13 +242,33 @@ static int menu_validate_number(struct symbol *sym, struct symbol *sym2)
 
 static void sym_check_prop(struct symbol *sym)
 {
-	struct property *prop;
+	struct property *prev, *prop;
 	struct symbol *sym2;
 	char *use;
 
 	for (prop = sym->prop; prop; prop = prop->next) {
 		switch (prop->type) {
 		case P_DEFAULT:
+			for_all_defaults(sym, prev) {
+				if (prev == prop)
+					break;
+				if (expr_is_yes(prev->visible.expr)) {
+					if (!expr_eq(prev->expr, prop->expr))
+						prop_warn(prop,
+							"default for '%s' is unreachable: earlier default at %s:%d is unconditional",
+							sym->name ? sym->name : "<choice>",
+							prev->filename, prev->lineno);
+					break;
+				}
+				if (expr_eq(prev->visible.expr, prop->visible.expr)) {
+					if (!expr_eq(prev->expr, prop->expr))
+						prop_warn(prop,
+							"default for '%s' has the same condition as the earlier default at %s:%d",
+							sym->name ? sym->name : "<choice>",
+							prev->filename, prev->lineno);
+					break;
+				}
+			}
 			if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
 			    prop->expr->type != E_SYMBOL)
 				prop_warn(prop,
diff --git a/scripts/kconfig/tests/warn_dead_default/Kconfig b/scripts/kconfig/tests/warn_dead_default/Kconfig
new file mode 100644
index 000000000000..adf421d73dbd
--- /dev/null
+++ b/scripts/kconfig/tests/warn_dead_default/Kconfig
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config A
+	bool
+
+config B
+	bool
+
+config UNCONDITIONAL
+	int
+	default 1
+	default 2
+
+config CONDITIONAL
+	int
+	default 1 if A
+	default 2 if A
+	default 3 if B
+
+config CONDITIONAL_COMMUTATIVE
+	int
+	default 1 if A && B
+	default 2 if B && A
+
+config CONTROL
+	int
+	default 1 if A
+	default 2 if B
+	default 3
+
+choice
+	prompt "test choice"
+	default C
+	default D
+
+	config C
+		bool "C"
+	config D
+		bool "D"
+endchoice
diff --git a/scripts/kconfig/tests/warn_dead_default/__init__.py b/scripts/kconfig/tests/warn_dead_default/__init__.py
new file mode 100644
index 000000000000..911b30ce19fe
--- /dev/null
+++ b/scripts/kconfig/tests/warn_dead_default/__init__.py
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+"""
+Test detection of dead defaults (different defaults that can never be active).
+"""
+
+def test(conf):
+    assert conf.olddefconfig() == 0
+    assert conf.stderr_contains('expected_stderr')
diff --git a/scripts/kconfig/tests/warn_dead_default/expected_stderr b/scripts/kconfig/tests/warn_dead_default/expected_stderr
new file mode 100644
index 000000000000..baa20bf33910
--- /dev/null
+++ b/scripts/kconfig/tests/warn_dead_default/expected_stderr
@@ -0,0 +1,4 @@
+Kconfig:12:warning: default for 'UNCONDITIONAL' is unreachable: earlier default at Kconfig:11 is unconditional
+Kconfig:17:warning: default for 'CONDITIONAL' has the same condition as the earlier default at Kconfig:16
+Kconfig:23:warning: default for 'CONDITIONAL_COMMUTATIVE' has the same condition as the earlier default at Kconfig:22
+Kconfig:34:warning: default for '<choice>' is unreachable: earlier default at Kconfig:33 is unconditional
-- 
2.53.0


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

end of thread, other threads:[~2026-06-06 17:36 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-06 14:00 [PATCH] kconfig: warn on dead default Julian Braha
2026-06-06 14:23 ` Sam Ravnborg
2026-06-06 15:01   ` Julian Braha
2026-06-06 17:36     ` Sam Ravnborg

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