From nobody Mon May 25 08:11:55 2026 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (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 747F1385D60 for ; Sat, 16 May 2026 21:54:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778968449; cv=none; b=NKIv+FCL0L4fuDW1hLC18bnhXhWwBwmEfG1UzP3k6xYuxN/Ofpsih+bwaWL6uuLI5m1fswRwzA44NgwF+ru4KKw8yzvg28HOWsiuixrY5/Z3Tbl+a1qObjFbM9+Rc0VHvQ0Y4HkxyG+q5Gy47hcqS+B4HP5Ta2J0aFSx2/aTJ0w= 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.51 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-f51.google.com with SMTP id 5b1f17b1804b1-488af96f6b2so11177085e9.0 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=g9rTfG58npM8/7nF9MaelIj+YMhURgljp6KfIJW8XzqGLk/ODWog3/HWipnkNkMiYm vCm8EpZoSIOw/90q3BuIB4bgcNHDlssayF4ltcKLlKJbMDixIW5kEIp3rgqtBJBBQBq2 sXBfnv5ewi504AiYaoL6pzOYETdFcfMeM+HJM8y9lprwKeFi995nhjdHt7FJxRvuTz17 kH16/ah7f4Z2t7EOV6X/O96Jzku6Tb4iDjt+sg2EepX5rNg9ubGkSC0+0CnpEL6gKYOL bg9EdasXkMTigxIw+rUxw9FYqjYN94JsEFQPHclFlmDRMfvIHOmvE1E/CqTM7uEeCiZg C5Zw== X-Forwarded-Encrypted: i=1; AFNElJ9F0gX8g4xHIJSetxC3cYm7FObnRBpdyI9/Dg6WkLJiYZhMovYbkqj0bbCTlpP6o4KMNpw14Jj8H1uY1fM=@vger.kernel.org X-Gm-Message-State: AOJu0YwMunxdRhcb+b5b5MXKUijKPef5IMfRC8Dud4DK1mRtyoIBmkMf Omrv8LqsZgi6bXotRNIIF4JwoTHjJASo58ihk70Vzd6KA2mhV0qpRIg5 X-Gm-Gg: Acq92OGbyG9QQaXnnjWHAQEQCSASWoCIDk/pN48IlSPF45ASJh0RI8NLKGCRYLHt6BJ XP9fnIoAjjWG3ez5Z9g/0dpiBSR4xEp03DTQ2TbIAPxQaSj3+Tsb3diURZ5EDUE4PDQ/6aEma+i YnDh5a5C3h/FooCNHT/AvHqzySbb4PDxd+Hgzx7YRGOOU/CbsPfjxIof7tg4TyX37ZI/u2JDkie xHamrDeYWXnQOl1Kz63fbx68bbedH2UkWWnixM0cna7duAjTkO2uhNyb5d8ar+zy0wMcG8NM3+I 6MZ8PHAACo4DMANmzrHoEVyn+ZZiCaXbCGh/e2Y/1j9Jqs7PjWAFUQhEQJbteK6a2g38wubyM0n rcsl653sFCfaEBc9MQJ0xBIDoLwqTDVAjZWk7c/hwvxJ5AxQd/ShfhzAk/ng9Zj6x/7jY4EyZMo 3DOC3ZkdfCKf5xQJ7peTBNUmXfWPhzdf7l/jFUqSNWD+gltf+A2yHIND8= 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-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 :=3D $(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 :=3D $(no-dot-config-targets) %install modules_sign= kernelrelease \ image_name single-targets :=3D %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.rsi %.s %/ @@ -536,6 +536,7 @@ OBJDUMP =3D $(CROSS_COMPILE)objdump READELF =3D $(CROSS_COMPILE)readelf STRIP =3D $(CROSS_COMPILE)strip endif +CARGO =3D cargo RUSTC =3D rustc RUSTDOC =3D rustdoc RUSTFMT =3D rustfmt @@ -633,7 +634,7 @@ export RUSTC_BOOTSTRAP :=3D 1 export CLIPPY_CONF_DIR :=3D $(srctree) =20 export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPI= LE 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 B= INDGEN LLVM_LINK export HOSTRUSTC KBUILD_HOSTRUSTFLAGS export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX Y= ACC AWK INSTALLKERNEL export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX @@ -1705,7 +1706,7 @@ MRPROPER_FILES +=3D 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 \ =20 # clean - Delete most, but leave enough to build external modules # @@ -2227,7 +2228,7 @@ endif # Scripts to check various things for consistency # ------------------------------------------------------------------------= --- =20 -PHONY +=3D includecheck versioncheck coccicheck +PHONY +=3D includecheck versioncheck coccicheck kconfirm =20 includecheck: find $(srctree)/* $(RCS_FIND_IGNORE) \ @@ -2242,6 +2243,12 @@ versioncheck: coccicheck: $(Q)$(BASH) $(srctree)/scripts/$@ =20 + +kconfirm: + $(Q)$(MAKE) -C $(srctree)/scripts/kconfirm srctree=3D$(abs_srctree) SRCAR= CH=3D$(SRCARCH) || \ + (printf "\n kconfirm failed to compile and run. Have you set up its dep= endencies yet?\n See Documentation/dev-tools/kconfirm.rst\n\n" && false) +clean-dirs +=3D scripts/kconfirm + PHONY +=3D checkstack kernelrelease kernelversion image_name =20 # 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) +=3D selinux subdir-$(CONFIG_SECURITY_IPE) +=3D ipe =20 # Let clean descend into subdirs -subdir- +=3D basic dtc gdb kconfig mod +subdir- +=3D 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 =3D 4 + +[[package]] +name =3D "bytecount" +version =3D "0.6.9" +source =3D "registry+https://github.com/rust-lang/crates.io-index" +checksum =3D "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0= 365e" + +[[package]] +name =3D "kconfirm-lib" +version =3D "0.10.0" +dependencies =3D [ + "nom-kconfig", +] + +[[package]] +name =3D "kconfirm-linux" +version =3D "0.10.0" +dependencies =3D [ + "kconfirm-lib", + "nom-kconfig", +] + +[[package]] +name =3D "memchr" +version =3D "2.8.0" +source =3D "registry+https://github.com/rust-lang/crates.io-index" +checksum =3D "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff= 3f79" + +[[package]] +name =3D "nom" +version =3D "8.0.0" +source =3D "registry+https://github.com/rust-lang/crates.io-index" +checksum =3D "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f= 9405" +dependencies =3D [ + "memchr", +] + +[[package]] +name =3D "nom-kconfig" +version =3D "0.11.0" +source =3D "registry+https://github.com/rust-lang/crates.io-index" +checksum =3D "5a0220bb2c8e5ad29b06fe0f75a276affeb10c9504726bf46d81fef78d69= b1e3" +dependencies =3D [ + "nom", + "nom_locate", +] + +[[package]] +name =3D "nom_locate" +version =3D "5.0.0" +source =3D "registry+https://github.com/rust-lang/crates.io-index" +checksum =3D "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68= 438d" +dependencies =3D [ + "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 =3D ["kconfirm-lib", "kconfirm-linux"] +resolver =3D "3" + +[workspace.package] +rust-version =3D "1.85.0" + +[workspace.dependencies] +nom-kconfig =3D { version =3D "0.11", default-features =3D false, features= =3D [ + "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 :=3D kconfirm + +# Extra arguments forwarded to kconfirm. +# Example: make kconfirm KCONFIRM_ARGS=3D"--enable-check dead_links" +KCONFIRM_ARGS ?=3D + +$(TARGET): + $(CARGO) run --release --offline -p kconfirm-linux -- --linux-path $(srct= ree) --enable-arch $(SRCARCH) $(KCONFIRM_ARGS) + + +clean-files +=3D target vendor diff --git a/scripts/kconfirm/kconfirm-lib/Cargo.toml b/scripts/kconfirm/kc= onfirm-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 =3D "kconfirm-lib" +version =3D "0.10.0" +edition =3D "2024" +rust-version.workspace =3D true + +[dependencies] +nom-kconfig =3D { workspace =3D true } + +[features] +default =3D [] diff --git a/scripts/kconfirm/kconfirm-lib/src/analyze.rs b/scripts/kconfir= m/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 he= lp 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 =3D=3D group =3D> {} + + // start of group + None =3D> { + self.current_group =3D Some(group); + } + + Some(current) =3D> { + // 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 =3D 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 =3D 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 =3D check_link(&link); + if status !=3D LinkStatus::Ok && status !=3D LinkStatus::Proba= blyBlocked { + 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 =3D 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 =3D Vec::new(); + + let ctx =3D 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 =3D entry.symbol; + + let mut child_ctx =3D ctx.child(); + + let mut config_type =3D None; + let mut kconfig_dependencies =3D Vec::new(); + let mut kconfig_selects: Vec