From 293d7d5a0b17cdfb68291acaa7757b28508c8c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Dr=C3=A9an?= Date: Fri, 20 Feb 2026 17:44:48 +0100 Subject: [PATCH 1/6] fix(missing_type): avoid false positives for missing let type on non-normal bindings --- .gitignore | 4 ++++ rules/missing_type/src/lib.rs | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ad67955..4782745 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# OS generated files +.DS_Store +Thumbs.db + # Generated by Cargo # will have compiled files and executables debug diff --git a/rules/missing_type/src/lib.rs b/rules/missing_type/src/lib.rs index 531d8e5..38453c4 100644 --- a/rules/missing_type/src/lib.rs +++ b/rules/missing_type/src/lib.rs @@ -7,7 +7,7 @@ extern crate rustc_middle; extern crate rustc_session; use rustc_errors::Diag; -use rustc_hir::{Body, Expr, ExprKind, LetStmt, PatKind}; +use rustc_hir::{Body, Expr, ExprKind, LetStmt, LocalSource, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext, LintStore}; use rustc_middle::ty::TyCtxt; use rustc_session::{Session, declare_lint, declare_lint_pass}; @@ -68,6 +68,16 @@ impl<'tcx> LateLintPass<'tcx> for MissingType { return; } + // Ignore non-normal let statements, such as those generated by the + // compiler for desugared constructs, as they may not have an explicit + // type annotation even if the original source code does. This + // check ensures that the lint only applies to normal let statements + // written by the user, and not to those generated by the compiler + // during desugaring. + if !matches!(local.source, LocalSource::Normal) { + return; + } + // Check if the let statement has an explicit type annotation. If not, // emit a warning. if local.ty.is_none() { From 9d6da6e9319bbe4ac850f2e17384c4904f6cbeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Dr=C3=A9an?= Date: Fri, 20 Feb 2026 17:48:42 +0100 Subject: [PATCH 2/6] fix(missing_type): properly filter macro-generated let bindings --- rules/missing_type/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/missing_type/src/lib.rs b/rules/missing_type/src/lib.rs index 38453c4..cf97bea 100644 --- a/rules/missing_type/src/lib.rs +++ b/rules/missing_type/src/lib.rs @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingType { // be possible to determine the type annotation in that case. // Ignore anything coming from macro expansion (async_trait, derives, // etc.) - if local.span.from_expansion() { + if !local.span.ctxt().outer_expn_data().is_root() { return; } From f7d2588161ab622e9489f3f8c90d24e981524db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Dr=C3=A9an?= Date: Fri, 20 Feb 2026 18:15:31 +0100 Subject: [PATCH 3/6] fix(missing_type): ignore macro-generated bodies in missing let type lint --- rules/missing_type/src/lib.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/rules/missing_type/src/lib.rs b/rules/missing_type/src/lib.rs index cf97bea..bf37d12 100644 --- a/rules/missing_type/src/lib.rs +++ b/rules/missing_type/src/lib.rs @@ -5,9 +5,10 @@ extern crate rustc_hir; extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_session; +extern crate rustc_span; use rustc_errors::Diag; -use rustc_hir::{Body, Expr, ExprKind, LetStmt, LocalSource, PatKind}; +use rustc_hir::{Body, Expr, ExprKind, LetStmt, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext, LintStore}; use rustc_middle::ty::TyCtxt; use rustc_session::{Session, declare_lint, declare_lint_pass}; @@ -55,11 +56,22 @@ impl<'tcx> LateLintPass<'tcx> for MissingType { if matches!(local.pat.kind, PatKind::Wild) { return; } + + let Some(body_id) = context.enclosing_body else { + return; + }; + + // Skip if the let statement is from a macro expansion, as it may not + // be possible to determine the type annotation in that case. + if context.tcx.hir_body(body_id).value.span.from_expansion() { + return; + } + // Skip if the let statement is from a macro expansion, as it may not // be possible to determine the type annotation in that case. // Ignore anything coming from macro expansion (async_trait, derives, // etc.) - if !local.span.ctxt().outer_expn_data().is_root() { + if local.span.from_expansion() { return; } @@ -68,16 +80,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingType { return; } - // Ignore non-normal let statements, such as those generated by the - // compiler for desugared constructs, as they may not have an explicit - // type annotation even if the original source code does. This - // check ensures that the lint only applies to normal let statements - // written by the user, and not to those generated by the compiler - // during desugaring. - if !matches!(local.source, LocalSource::Normal) { - return; - } - // Check if the let statement has an explicit type annotation. If not, // emit a warning. if local.ty.is_none() { From 90d5ed2635dd40582f17f0c99dd06928077666ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Dr=C3=A9an?= Date: Fri, 20 Feb 2026 18:28:58 +0100 Subject: [PATCH 4/6] chore: enhanced code according to ci rules, improved ci workflow --- .github/workflows/ci.yaml | 50 +++++++-------- deny.toml | 5 -- rules/indexing_usage/src/lib.rs | 106 +++++++++++++++----------------- rules/missing_type/src/lib.rs | 4 +- rules/panic_usage/src/lib.rs | 26 ++++---- 5 files changed, 90 insertions(+), 101 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 57fcb6a..083454b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,41 +5,43 @@ on: branches: [master] pull_request: +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: "-D warnings" + jobs: ci: - name: Continuous Integration + name: Dylint (nightly) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - pkg-config \ - libssl-dev \ - libgit2-dev \ - libcurl4-openssl-dev \ - build-essential - - - uses: dtolnay/rust-toolchain@nightly + - uses: actions/checkout@v6 + + - uses: dtolnay/rust-toolchain@v1 with: + toolchain: nightly components: rustc-dev,rust-src,llvm-tools-preview - - uses: actions/cache@v4 + - run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH + + - uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - ~/.dylint_drivers - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- + cache-on-failure: true + + - uses: cargo-bins/cargo-binstall@main - name: Install Dylint - run: cargo install cargo-dylint dylint-link --locked + run: cargo binstall -y cargo-dylint dylint-link - name: Run Dylint run: cargo dylint --workspace --all + + - name: Run CI + run: cargo make ci diff --git a/deny.toml b/deny.toml index 1faf1e1..15a591c 100644 --- a/deny.toml +++ b/deny.toml @@ -3,12 +3,7 @@ confidence-threshold = 0.9 allow = [ "MIT", "Apache-2.0", - "BSD-2-Clause", - "BSD-3-Clause", - "ISC", - "Zlib", "Unicode-3.0", - "CDLA-Permissive-2.0", ] [bans] diff --git a/rules/indexing_usage/src/lib.rs b/rules/indexing_usage/src/lib.rs index ac50efc..c65927c 100644 --- a/rules/indexing_usage/src/lib.rs +++ b/rules/indexing_usage/src/lib.rs @@ -33,51 +33,47 @@ impl<'tcx> LateLintPass<'tcx> for SecurityIndexingUsage { context: &LateContext<'tcx>, expression: &'tcx Expr<'tcx>, ) { - match &expression.kind { - ExprKind::Index(_, index_expr, _) => { - match &index_expr.kind { - // Literal indexing: array[0]. - ExprKind::Lit(_) => { - context.span_lint( - SECURITY_INDEXING_USAGE, - expression.span, - |diagnostic: &mut Diag<'_, ()>| { - diagnostic.primary_message( - "Usage of indexing operation detected.", - ); - }, - ); - }, + if let ExprKind::Index(_, index_expr, _) = &expression.kind { + match &index_expr.kind { + // Literal indexing: array[0]. + ExprKind::Lit(_) => { + context.span_lint( + SECURITY_INDEXING_USAGE, + expression.span, + |diagnostic: &mut Diag<'_, ()>| { + diagnostic.primary_message( + "Usage of indexing operation detected.", + ); + }, + ); + }, - // Range slicing: array[1..], array[..], array[a..b]. - ExprKind::Struct(_, _, _) => { - context.span_lint( - SECURITY_INDEXING_USAGE, - expression.span, - |diagnostic: &mut Diag<'_, ()>| { - diagnostic.primary_message( - "Usage of slicing operation detected.", - ); - }, - ); - }, + // Range slicing: array[1..], array[..], array[a..b]. + ExprKind::Struct(_, _, _) => { + context.span_lint( + SECURITY_INDEXING_USAGE, + expression.span, + |diagnostic: &mut Diag<'_, ()>| { + diagnostic.primary_message( + "Usage of slicing operation detected.", + ); + }, + ); + }, - // Any other dynamic indexing: array[i]. - _ => { - context.span_lint( - SECURITY_INDEXING_USAGE, - expression.span, - |diagnostic: &mut Diag<'_, ()>| { - diagnostic.primary_message( - "Usage of indexing operation detected.", - ); - }, - ); - }, - } - }, - - _ => {}, + // Any other dynamic indexing: array[i]. + _ => { + context.span_lint( + SECURITY_INDEXING_USAGE, + expression.span, + |diagnostic: &mut Diag<'_, ()>| { + diagnostic.primary_message( + "Usage of indexing operation detected.", + ); + }, + ); + }, + } } } @@ -97,20 +93,18 @@ impl<'tcx> LateLintPass<'tcx> for SecurityIndexingUsage { if let ItemKind::Impl(implementation) = &item.kind && let Some(trait_ref) = implementation.of_trait && let Some(def_id) = trait_ref.trait_ref.path.res.opt_def_id() + && (context.tcx.lang_items().index_trait() == Some(def_id) + || context.tcx.lang_items().index_mut_trait() == Some(def_id)) { - if context.tcx.lang_items().index_trait() == Some(def_id) - || context.tcx.lang_items().index_mut_trait() == Some(def_id) - { - context.span_lint( - SECURITY_INDEXING_USAGE, - item.span, - |diagnostic: &mut Diag<'_, ()>| { - diagnostic.primary_message( - "Implementation of Index/IndexMut trait detected.", - ); - }, - ); - } + context.span_lint( + SECURITY_INDEXING_USAGE, + item.span, + |diagnostic: &mut Diag<'_, ()>| { + diagnostic.primary_message( + "Implementation of Index/IndexMut trait detected.", + ); + }, + ); } } } diff --git a/rules/missing_type/src/lib.rs b/rules/missing_type/src/lib.rs index bf37d12..a0a50db 100644 --- a/rules/missing_type/src/lib.rs +++ b/rules/missing_type/src/lib.rs @@ -8,7 +8,7 @@ extern crate rustc_session; extern crate rustc_span; use rustc_errors::Diag; -use rustc_hir::{Body, Expr, ExprKind, LetStmt, PatKind}; +use rustc_hir::{Body, BodyId, Expr, ExprKind, LetStmt, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext, LintStore}; use rustc_middle::ty::TyCtxt; use rustc_session::{Session, declare_lint, declare_lint_pass}; @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingType { return; } - let Some(body_id) = context.enclosing_body else { + let Some(body_id): Option = context.enclosing_body else { return; }; diff --git a/rules/panic_usage/src/lib.rs b/rules/panic_usage/src/lib.rs index 367941a..453a029 100644 --- a/rules/panic_usage/src/lib.rs +++ b/rules/panic_usage/src/lib.rs @@ -75,22 +75,20 @@ impl<'tcx> LateLintPass<'tcx> for SecurityPanicUsage { && let Some(def_id) = context .typeck_results() .type_dependent_def_id(expression.hir_id) - { - if context.tcx.is_diagnostic_item(sym::unwrap, def_id) + && (context.tcx.is_diagnostic_item(sym::unwrap, def_id) || context.tcx.is_diagnostic_item(sym::option_unwrap, def_id) || context.tcx.is_diagnostic_item(sym::except, def_id) - || context.tcx.is_diagnostic_item(sym::option_expect, def_id) - { - context.span_lint( - SECURITY_PANIC_USAGE, - expression.span, - |diagnostic: &mut Diag<'_, ()>| { - diagnostic.primary_message( - "Call to panic backend `unwrap/expect` detected.", - ); - }, - ); - } + || context.tcx.is_diagnostic_item(sym::option_expect, def_id)) + { + context.span_lint( + SECURITY_PANIC_USAGE, + expression.span, + |diagnostic: &mut Diag<'_, ()>| { + diagnostic.primary_message( + "Call to panic backend `unwrap/expect` detected.", + ); + }, + ); } // Detect calls to panic-related functions in the standard library. From eba68aafb666452e2aeb6896c453581ec680e80c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Dr=C3=A9an?= Date: Fri, 20 Feb 2026 21:51:28 +0100 Subject: [PATCH 5/6] feat: updated ci workflow --- .github/workflows/ci.yaml | 19 ++++++++++--------- .vscode/settings.json | 1 + Makefile.toml | 1 + 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 083454b..df5284c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,21 +27,22 @@ jobs: - uses: dtolnay/rust-toolchain@v1 with: toolchain: nightly - components: rustc-dev,rust-src,llvm-tools-preview + components: rustfmt, clippy, rustc-dev, rust-src, llvm-tools-preview - - run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH + - name: Ensure cargo in PATH + run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - uses: cargo-bins/cargo-binstall@main - - name: Install Dylint - run: cargo binstall -y cargo-dylint dylint-link - - - name: Run Dylint - run: cargo dylint --workspace --all + - run: | + cargo binstall -y \ + cargo-make \ + cargo-deny \ + cargo-audit \ + cargo-dylint \ + dylint-link - name: Run CI run: cargo make ci diff --git a/.vscode/settings.json b/.vscode/settings.json index 4d3b473..ebfed16 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,6 +30,7 @@ "dylint", "krate", "qpath", + "rustflags", "rustsec", "rustup", "typeck", diff --git a/Makefile.toml b/Makefile.toml index 35f775e..53f22b3 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -115,5 +115,6 @@ args = [ "ubuntu-latest=ghcr.io/catthehacker/ubuntu:full-latest", "--container-architecture", "linux/amd64", + "--rm", ] workspace = false From c1f6cf88b439f5ce85879ff33076773daf5544e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20Dr=C3=A9an?= Date: Fri, 20 Feb 2026 22:21:05 +0100 Subject: [PATCH 6/6] fix(ci): removed global rust arguments to allow dylint iui tests to build correctly --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index df5284c..d5f84d2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,7 +14,6 @@ permissions: env: CARGO_TERM_COLOR: always - RUSTFLAGS: "-D warnings" jobs: ci: @@ -36,7 +35,8 @@ jobs: - uses: cargo-bins/cargo-binstall@main - - run: | + - name: Install dependencies + run: | cargo binstall -y \ cargo-make \ cargo-deny \