From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f50.google.com (mail-wm1-f50.google.com [209.85.128.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 75244385D77 for ; Sat, 16 May 2026 21:54:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.50 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778968449; cv=none; b=EPMQuiWpRpP0twAvAAqFQMBXl6ZjkRJCCleIcmypT8NHxYOj6YTREFXLXLIzzb0ihCda41BotValSwdb3BJzV60NTylBtc7NPW6ck/93Jj0xUO7+yb0LGeBUwW/4nwKFTWbxQxB7+8Z+YnZLAy2fl2p/ar0WiXdjDBkXGpAVouU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778968449; c=relaxed/simple; bh=4vFG7wSzURj3z+ZwzMZFivtg1MXtx2E3JNCXm9FZpCE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=G3Qp6pVeRkACS1HNHTO0HS6goML6Rv4yNiS+m7ActYvK7tD1ONY4RbDjoFpP3JCfB8rF/NOgDDL9+AARVgC/DbROq5Dzkz941XEREnxSb7OwM8nRv/P30zREPd10MOgsbWYhNG02G5OCJtS5HKGwSjUVDE84CXuZEgHeakUmXBE= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=PEmOFGIy; arc=none smtp.client-ip=209.85.128.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="PEmOFGIy" Received: by mail-wm1-f50.google.com with SMTP id 5b1f17b1804b1-488b150559bso7512635e9.1 for ; Sat, 16 May 2026 14:54:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778968442; x=1779573242; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=6+yolIm7RX9E2opLcdPDNyFMzlkMNsEakK2aSVc4NP8=; b=PEmOFGIytf2sAo8DCoSUzZ7eQR/pgrQQktxLpheLyGeOkDdKs0K7u2ss3QQMkWi5Jo /xx1g1k9+cZka0scmUTD9vKUjx50BvlGmCGppQNpgNUAZy0ALml6csfD3oF2erO3C1/1 8DhekZksJweC348pnMbXZyR4ArFn6nubgHM4OljUZCesnPR1szBo+FXHRI5WtiDmXgzE ZN8aGETERB1i55TnIsRdk9cdowiOhWGqCQvK+xR8QIiVqlx8IsDZO7rzmBgvvzPuPTng q3KHg1hNhC1xRTJIa7cfkWr5ZlvNUO7WTuVyESJ8TYOLbdtnT6q4NtXRE89ASnidomx4 /z9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778968442; x=1779573242; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=6+yolIm7RX9E2opLcdPDNyFMzlkMNsEakK2aSVc4NP8=; b=hkZ9Fykh09aMrQyblJO/QwJZECxbPZZsYGpJMKQa6SykvmbKNFjTjUuiN7+A0XviHR 0FVOK/2hJVnoN/nHbdbAoPY46jIJFpuPWWFfnYd8/nwWPipuz1PwIOkYOGJgzl2zNWkI 4oGxmtdgVmNcfio075X2Gx2qFbPFKoo56M6GpoOkUq2uaqcSMbiPiKnypz55lDZT29Tf lpe6JsthJOyzk40OmjV/HEptUk5XUuNrkSFNxS1brDpHvYMfZmszw6tn+pUlT17QsOo7 pvSxOZk6LISfow5PCbkWPdbRp+JqVoWDRE/RRCFCIIJtKodH3yHP+XZEws3kmP9KTtPZ 1e1g== X-Forwarded-Encrypted: i=1; AFNElJ+VVmu9W3RCrTLpjSODfm4enlElyafPNAJqWX+lHv8hCP5DSucGrjVrhT1q/NUK7CfPZ16eW7+mHe0=@vger.kernel.org X-Gm-Message-State: AOJu0Yy1ZweZEENFJNmAPiaLIOZ4ccyAKVRg3jOCB7TXjq6lPRUL01KC Qy5XELIAikEavSB8d/6Eqs6Yb42trt//PioGEEA0Hi+T5PPhh52EjQyB X-Gm-Gg: Acq92OE/kconv5JoclEuA01S8rbpkOCXVJwIB2ba7MIfUmdXQ4u1gWYx8WsqzVMN02J 7HQNCaeDuBjf2XX/6+q++3Cw6C52mMK3nLzi0317B6lQSZ4MurXx4BBY+SuaRq6kdmHEFi/f+tr QMYOcC5EvPuc/YGmYBC3bk0FbvT//+hqVxuUcF6F69GysmMCOUJWAlKGs4bn9BynZBZD/S1oYpP 7F1f7H+Rw2tajOwNlFz3VM9w94LB/Gu3+bduY9XgPa3UroxrNAFDlW2bLHLxzuNUJq4vYcp1q8U k+SgLFojSnzIfAxiSTjzNeABo810nclZfPUlEn/FomvNSRtz64VlTuQOm7uCyCK8RVbmL2DGz5S YsmWzyAf7JRJXr8bWD8YOb0mfItPO4msVT9pW7kIoGVn+X7p1ck28P/73NW1rCb243/rhm4uxrM 3aspLRXMGVnuPo39W7q6MuV4xWacpwx1+HIfbbo3xZexCyRAHHCagZjTo= X-Received: by 2002:a05:600c:c087:b0:48a:7b55:12a6 with SMTP id 5b1f17b1804b1-48fe5cb36aamr105981905e9.0.1778968441130; Sat, 16 May 2026 14:54:01 -0700 (PDT) Received: from nixos-office (195-23-151-163.net.novis.pt. [195.23.151.163]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48fe4c90b27sm158383415e9.8.2026.05.16.14.53.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 May 2026 14:54:00 -0700 (PDT) Sender: Julian Braha From: Julian Braha To: nathan@kernel.org, nsc@kernel.org Cc: jani.nikula@linux.intel.com, akpm@linux-foundation.org, gary@garyguo.net, ljs@kernel.org, arnd@arndb.de, gregkh@linuxfoundation.org, masahiroy@kernel.org, ojeda@kernel.org, corbet@lwn.net, qingfang.deng@linux.dev, yann.prono@telecomnancy.net, demiobenour@gmail.com, ej@inai.de, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, Julian Braha Subject: [RFC PATCH v3 1/3] scripts: add kconfirm Date: Sat, 16 May 2026 22:53:52 +0100 Message-ID: <20260516215354.449807-2-julianbraha@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260516215354.449807-1-julianbraha@gmail.com> References: <20260516215354.449807-1-julianbraha@gmail.com> Precedence: bulk X-Mailing-List: linux-doc@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add kconfirm into scripts/ kconfirm is a static analysis tool with various checks for Kconfig, and intended to have zero false alarms by default. These default checks currently include dead code, constant conditions, and invalid (reverse) ranges. There are also optional checks for dead links in the help texts, and for config options that select visible config options. Checks are performed on the same architecture as the kernel build, using a single thread. More architectures can be enabled by passing `--enable-arch`. Alarms are tagged using the architectures' config options, like so: [X86] if specific to x86, or [X86, ARM] if the alarm appears for both x86 and arm. Each alarm gets a single line (deduplicated across architectures) and is formatted like this: [] [, ] config : The tool source contains two Rust packages: kconfirm-lib and kconfirm-linux. kconfirm-lib is the underlying library that analyzes Kconfig code, and formats alarms for usability. It analyzes the entire Linux Kconfig spec, including all architectures. This package exposes the symbol table that it constructs so that other tools can import this library, and make use of it for their own Kconfig analyses. kconfirm-linux imports kconfirm-lib, and provides the CLI, which is intended for either manual usage, or integration with the Linux build system so that users can simply run `make kconfirm` from the root. kconfirm-linux also handles some of the specificities of how Kconfig is used in the Linux tree, in contrast to other open source software. E.g. the way that each architecture has its own Kconfig and Kconfig.debug files. The tool's dependencies need to be downloaded from crates.io by running `cargo vendor` in scripts/kconfirm/ before building and running the tool. Signed-off-by: Julian Braha --- Makefile | 15 +- scripts/Makefile | 2 +- scripts/kconfirm/.gitignore | 3 + scripts/kconfirm/Cargo.lock | 60 ++ scripts/kconfirm/Cargo.toml | 12 + scripts/kconfirm/Makefile | 14 + scripts/kconfirm/kconfirm-lib/Cargo.toml | 12 + scripts/kconfirm/kconfirm-lib/src/analyze.rs | 643 ++++++++++++++++ scripts/kconfirm/kconfirm-lib/src/checks.rs | 701 ++++++++++++++++++ scripts/kconfirm/kconfirm-lib/src/curl_ffi.rs | 182 +++++ .../kconfirm/kconfirm-lib/src/dead_links.rs | 138 ++++ scripts/kconfirm/kconfirm-lib/src/lib.rs | 62 ++ scripts/kconfirm/kconfirm-lib/src/output.rs | 111 +++ .../kconfirm/kconfirm-lib/src/symbol_table.rs | 223 ++++++ scripts/kconfirm/kconfirm-linux/Cargo.toml | 10 + .../kconfirm/kconfirm-linux/src/getopt_ffi.rs | 99 +++ scripts/kconfirm/kconfirm-linux/src/lib.rs | 78 ++ scripts/kconfirm/kconfirm-linux/src/main.rs | 192 +++++ 18 files changed, 2552 insertions(+), 5 deletions(-) create mode 100644 scripts/kconfirm/.gitignore create mode 100644 scripts/kconfirm/Cargo.lock create mode 100644 scripts/kconfirm/Cargo.toml create mode 100644 scripts/kconfirm/Makefile create mode 100644 scripts/kconfirm/kconfirm-lib/Cargo.toml create mode 100644 scripts/kconfirm/kconfirm-lib/src/analyze.rs create mode 100644 scripts/kconfirm/kconfirm-lib/src/checks.rs create mode 100644 scripts/kconfirm/kconfirm-lib/src/curl_ffi.rs create mode 100644 scripts/kconfirm/kconfirm-lib/src/dead_links.rs create mode 100644 scripts/kconfirm/kconfirm-lib/src/lib.rs create mode 100644 scripts/kconfirm/kconfirm-lib/src/output.rs create mode 100644 scripts/kconfirm/kconfirm-lib/src/symbol_table.rs create mode 100644 scripts/kconfirm/kconfirm-linux/Cargo.toml create mode 100644 scripts/kconfirm/kconfirm-linux/src/getopt_ffi.rs create mode 100644 scripts/kconfirm/kconfirm-linux/src/lib.rs create mode 100644 scripts/kconfirm/kconfirm-linux/src/main.rs diff --git a/Makefile b/Makefile index b7b80e84e1eb..99aaed5bdbc5 100644 --- a/Makefile +++ b/Makefile @@ -296,7 +296,7 @@ no-dot-config-targets := $(clean-targets) \ $(version_h) headers headers_% archheaders archscripts \ %asm-generic kernelversion %src-pkg dt_binding_check \ outputmakefile rustavailable rustfmt rustfmtcheck \ - run-command + run-command kconfirm no-sync-config-targets := $(no-dot-config-targets) %install modules_sign kernelrelease \ image_name single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.rsi %.s %/ @@ -536,6 +536,7 @@ OBJDUMP = $(CROSS_COMPILE)objdump READELF = $(CROSS_COMPILE)readelf STRIP = $(CROSS_COMPILE)strip endif +CARGO = cargo RUSTC = rustc RUSTDOC = rustdoc RUSTFMT = rustfmt @@ -633,7 +634,7 @@ export RUSTC_BOOTSTRAP := 1 export CLIPPY_CONF_DIR := $(srctree) export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC HOSTPKG_CONFIG -export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN LLVM_LINK +export CARGO RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN LLVM_LINK export HOSTRUSTC KBUILD_HOSTRUSTFLAGS export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX @@ -1705,7 +1706,7 @@ MRPROPER_FILES += include/config include/generated \ vmlinux-gdb.py \ rpmbuild \ rust/libmacros.so rust/libmacros.dylib \ - rust/libpin_init_internal.so rust/libpin_init_internal.dylib + rust/libpin_init_internal.so rust/libpin_init_internal.dylib \ # clean - Delete most, but leave enough to build external modules # @@ -2227,7 +2228,7 @@ endif # Scripts to check various things for consistency # --------------------------------------------------------------------------- -PHONY += includecheck versioncheck coccicheck +PHONY += includecheck versioncheck coccicheck kconfirm includecheck: find $(srctree)/* $(RCS_FIND_IGNORE) \ @@ -2242,6 +2243,12 @@ versioncheck: coccicheck: $(Q)$(BASH) $(srctree)/scripts/$@ + +kconfirm: + $(Q)$(MAKE) -C $(srctree)/scripts/kconfirm srctree=$(abs_srctree) SRCARCH=$(SRCARCH) || \ + (printf "\n kconfirm failed to compile and run. Have you set up its dependencies yet?\n See Documentation/dev-tools/kconfirm.rst\n\n" && false) +clean-dirs += scripts/kconfirm + PHONY += checkstack kernelrelease kernelversion image_name # UML needs a little special treatment here. It wants to use the host diff --git a/scripts/Makefile b/scripts/Makefile index 3434a82a119f..460655bd2de1 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -66,4 +66,4 @@ subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_SECURITY_IPE) += ipe # Let clean descend into subdirs -subdir- += basic dtc gdb kconfig mod +subdir- += basic dtc gdb kconfig kconfirm mod diff --git a/scripts/kconfirm/.gitignore b/scripts/kconfirm/.gitignore new file mode 100644 index 000000000000..f63ee0251591 --- /dev/null +++ b/scripts/kconfirm/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +/target +/vendor diff --git a/scripts/kconfirm/Cargo.lock b/scripts/kconfirm/Cargo.lock new file mode 100644 index 000000000000..d90bc7d2e2a3 --- /dev/null +++ b/scripts/kconfirm/Cargo.lock @@ -0,0 +1,60 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + +[[package]] +name = "kconfirm-lib" +version = "0.10.0" +dependencies = [ + "nom-kconfig", +] + +[[package]] +name = "kconfirm-linux" +version = "0.10.0" +dependencies = [ + "kconfirm-lib", + "nom-kconfig", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom-kconfig" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0220bb2c8e5ad29b06fe0f75a276affeb10c9504726bf46d81fef78d69b1e3" +dependencies = [ + "nom", + "nom_locate", +] + +[[package]] +name = "nom_locate" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d" +dependencies = [ + "bytecount", + "memchr", + "nom", +] diff --git a/scripts/kconfirm/Cargo.toml b/scripts/kconfirm/Cargo.toml new file mode 100644 index 000000000000..5880b06c4116 --- /dev/null +++ b/scripts/kconfirm/Cargo.toml @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +[workspace] +members = ["kconfirm-lib", "kconfirm-linux"] +resolver = "3" + +[workspace.package] +rust-version = "1.85.0" + +[workspace.dependencies] +nom-kconfig = { version = "0.11", default-features = false, features = [ + "display", +] } diff --git a/scripts/kconfirm/Makefile b/scripts/kconfirm/Makefile new file mode 100644 index 000000000000..6a0b7389103e --- /dev/null +++ b/scripts/kconfirm/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# kconfirm makefile + +TARGET := kconfirm + +# Extra arguments forwarded to kconfirm. +# Example: make kconfirm KCONFIRM_ARGS="--enable-check dead_links" +KCONFIRM_ARGS ?= + +$(TARGET): + $(CARGO) run --release --offline -p kconfirm-linux -- --linux-path $(srctree) --enable-arch $(SRCARCH) $(KCONFIRM_ARGS) + + +clean-files += target vendor diff --git a/scripts/kconfirm/kconfirm-lib/Cargo.toml b/scripts/kconfirm/kconfirm-lib/Cargo.toml new file mode 100644 index 000000000000..dd3d7cb1aa1d --- /dev/null +++ b/scripts/kconfirm/kconfirm-lib/Cargo.toml @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +[package] +name = "kconfirm-lib" +version = "0.10.0" +edition = "2024" +rust-version.workspace = true + +[dependencies] +nom-kconfig = { workspace = true } + +[features] +default = [] diff --git a/scripts/kconfirm/kconfirm-lib/src/analyze.rs b/scripts/kconfirm/kconfirm-lib/src/analyze.rs new file mode 100644 index 000000000000..24798581dc3d --- /dev/null +++ b/scripts/kconfirm/kconfirm-lib/src/analyze.rs @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0-only +use crate::AnalysisArgs; +use crate::Check; +use crate::SymbolTable; +use crate::dead_links; +use crate::dead_links::LinkStatus; +use crate::dead_links::check_link; +use crate::output::Finding; +use crate::output::Severity; +use crate::symbol_table::ChoiceData; +use nom_kconfig::Attribute::*; +use nom_kconfig::Entry; +use nom_kconfig::attribute::DefaultAttribute; +use nom_kconfig::attribute::Expression; +use nom_kconfig::attribute::Imply; +use nom_kconfig::attribute::Select; +use nom_kconfig::attribute::r#type::Type; +use nom_kconfig::entry::Choice; +use nom_kconfig::entry::Config; +use nom_kconfig::entry::If; +use nom_kconfig::entry::Menu; +use nom_kconfig::entry::Source; +use std::collections::HashSet; +use std::option::Option; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum FunctionalAttributes { + // only tracking the attributes that affect the semantics, e.g. not help texts + Dependencies, + Selects, + Implies, + Ranges, + Defaults, +} + +struct AttributeGroupingChecker { + current_group: Option, + finished_groups: HashSet, +} + +impl AttributeGroupingChecker { + fn new() -> Self { + Self { + current_group: None, + finished_groups: HashSet::new(), + } + } + + // doesn't modify `findings` if the style check is disabled + fn check( + &mut self, + group: FunctionalAttributes, + args: &AnalysisArgs, + findings: &mut Vec, + symbol: &str, + arch: &String, + message: String, + ) { + if !args.is_enabled(Check::UngroupedAttribute) { + return; + } + + match self.current_group { + // still contiguous + Some(current) if current == group => {} + + // start of group + None => { + self.current_group = Some(group); + } + + Some(current) => { + // the previous group finished + self.finished_groups.insert(current); + + // we've already finished this group, it's ungrouped + if self.finished_groups.contains(&group) { + findings.push(Finding { + severity: Severity::Style, + check: Check::UngroupedAttribute, + symbol: Some(symbol.to_string()), + message, + arch: arch.to_owned(), + }); + } + + // switch to the new group + self.current_group = Some(group); + } + } + } +} + +struct DeadLinkChecker { + visited_links: HashSet, +} + +impl DeadLinkChecker { + fn new() -> Self { + Self { + visited_links: HashSet::new(), + } + } + + fn check_text( + &mut self, + text: &str, + args: &AnalysisArgs, + findings: &mut Vec, + symbol: Option<&str>, + arch: &String, + context: &str, + ) { + if !args.is_enabled(Check::DeadLink) { + return; + } + + let links = dead_links::find_links(text); + + if links.is_empty() { + return; + } + + for link in links { + // avoid rechecking identical links + if !self.visited_links.insert(link.clone()) { + continue; + } + + let status = check_link(&link); + if status != LinkStatus::Ok && status != LinkStatus::ProbablyBlocked { + findings.push(Finding { + severity: Severity::Warning, + check: Check::DeadLink, + symbol: symbol.map(|s| s.to_string()), + message: format!( + "{} contains link {} with status {:?}", + context, link, status + ), + arch: arch.to_owned(), + }); + } + } + } +} + +#[derive(Clone)] +pub struct Context { + pub arch: String, + pub definition_condition: Vec, + pub visibility: Vec>, + pub dependencies: Vec, + pub in_choice: bool, +} + +impl Context { + fn with_arch(arch: String) -> Context { + Context { + arch, + definition_condition: vec![], + visibility: vec![], + dependencies: vec![], + in_choice: false, + } + } + + fn child(&self) -> Self { + self.clone() + } + + fn with_dep(mut self, dep: Expression) -> Self { + self.dependencies.push(dep); + self + } + + fn with_visibility(mut self, cond: Option) -> Self { + self.visibility.push(cond); + self + } + + fn with_definition(mut self, cond: Expression) -> Self { + self.definition_condition.push(cond); + self + } + + fn in_choice(mut self) -> Self { + self.in_choice = true; + self + } +} + +fn recurse_entries( + args: &AnalysisArgs, + symtab: &mut SymbolTable, + entries: Vec, + ctx: Context, + findings: &mut Vec, +) { + for entry in entries { + process_entry(args, symtab, entry, ctx.clone(), findings); + } +} + +pub fn analyze( + args: &AnalysisArgs, + symtab: &mut SymbolTable, + arch: String, + entries: Vec, +) -> Vec { + let mut findings = Vec::new(); + + let ctx = Context::with_arch(arch); + + recurse_entries(args, symtab, entries, ctx, &mut findings); + + findings +} + +fn handle_config( + args: &AnalysisArgs, + symtab: &mut SymbolTable, + entry: Config, + ctx: &Context, + findings: &mut Vec, +) { + let config_symbol = entry.symbol; + + let mut child_ctx = ctx.child(); + + let mut config_type = None; + let mut kconfig_dependencies = Vec::new(); + let mut kconfig_selects: Vec